@struktoai/mirage-core
Version:
A Unified Virtual Filesystem For AI Agents
172 lines • 8.33 kB
JavaScript
// ========= Copyright 2026 @ Strukto.AI All Rights Reserved. =========
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
// ========= Copyright 2026 @ Strukto.AI All Rights Reserved. =========
import { NotionConfig } from "../../../../core/notion/config.js";
import { Argument, CommandSpec, UsageStyle } from "../../../spec/types.js";
import { CLI, CLIHandler } from "../../types.js";
import { api } from "./api.js";
import { token } from "./auth/token.js";
import { query } from "./datasources/query.js";
import { resolve } from "./datasources/resolve.js";
import { guarded } from "./failure.js";
import { create } from "./pages/create.js";
import { edit } from "./pages/edit.js";
import { get } from "./pages/get.js";
import { trash } from "./pages/trash.js";
import { ntnAccessor } from "./util.js";
import { whoami } from "./whoami.js";
// Operand names are upstream's, verbatim: they are what the refusal for a
// missing one prints, so they are part of the grammar rather than
// documentation. Each verb names its own, which is why there is no one shared
// ID slot.
const PAGE_ID = new Argument('PAGE_ID');
const DATA_SOURCE_ID = new Argument('ID_OR_URL');
const DATABASE_ID = new Argument('ID');
const API_PATH = new Argument('PATH', { nargs: '*' });
const jsonOut = () => new Argument('--json', { action: 'store_true', help: 'Output the raw API response as JSON' });
const plain = () => new Argument('--plain', {
action: 'store_true',
help: 'Output as tab-separated values with no headers',
});
// NOTION_API_VERSION is upstream's own environment fallback, and naming it
// here is what makes the flag real: the executor fills the value from the
// session, so a leaf reads one flag rather than a flag and a fallback, and a
// usage line counts the option as supplied the way clap does.
const notionVersion = () => new Argument('--notion-version', {
metavar: 'VERSION',
env: 'NOTION_API_VERSION',
help: 'Override the Notion-Version header',
});
const content = () => new Argument('--content', { help: 'Markdown body (also read from stdin)' });
// The ntn program tree, matching the official Notion CLI's grammar verb for
// verb: ids are positional, `pages get` renders Markdown with a frontmatter
// title, and the REST surface that has no typed verb is reached through
// `ntn api` exactly as upstream reaches it. There is no `ntn blocks`/`ntn
// comments`/`ntn search`; those are `ntn api v1/blocks/...`, `ntn api
// v1/comments` and `ntn api v1/search`. Upstream's interactive and deploy
// verbs (`login`, `logout`, `update`, `workers`, `notion-as-code`, `doctor`,
// `files`) are out of scope for a virtualized CLI. Install with a NotionConfig.
export const NTN = new CLI({
spec: new CommandSpec({
name: 'ntn',
description: 'Notion CLI (Beta)',
usageStyle: UsageStyle.CLAP,
subcommands: [
new CommandSpec({
name: 'api',
description: 'Call the public Notion API (beta)',
arguments: [
new Argument(['-d', '--data'], { help: 'Use a JSON string as the request body' }),
new Argument(['-X', '--method'], { help: 'Override the inferred HTTP method' }),
notionVersion(),
API_PATH,
],
}),
new CommandSpec({
name: 'auth',
description: 'Inspect authentication credentials',
subcommands: [
new CommandSpec({ name: 'token', description: 'Print the current authentication token' }),
],
}),
new CommandSpec({
name: 'datasources',
description: 'Manage data sources',
subcommands: [
new CommandSpec({
name: 'query',
description: 'Query pages in a data source',
arguments: [
new Argument('--limit', { type: 'int', help: 'Maximum rows to return' }),
new Argument('--start-cursor', { help: 'Cursor to resume from' }),
new Argument(['-s', '--sort'], {
action: 'append',
metavar: 'SPEC',
help: "'<property> [asc|desc]'",
}),
new Argument('--filter', { metavar: 'JSON', help: 'Filter as a JSON object' }),
new Argument('--filter-file', {
type: 'path',
metavar: 'PATH',
help: 'Read the filter from a file',
}),
jsonOut(),
plain(),
notionVersion(),
DATA_SOURCE_ID,
],
}),
new CommandSpec({
name: 'resolve',
description: 'Resolve a Notion database ID to its data source IDs',
arguments: [jsonOut(), notionVersion(), DATABASE_ID],
}),
],
}),
new CommandSpec({
name: 'pages',
description: 'Manage pages',
subcommands: [
new CommandSpec({
name: 'get',
description: 'Retrieve a page as Markdown',
arguments: [jsonOut(), notionVersion(), PAGE_ID],
}),
new CommandSpec({
name: 'create',
description: 'Create a page from Markdown content',
arguments: [
content(),
new Argument('--parent', { help: 'page:<id>, database:<id>, or data-source:<id>' }),
jsonOut(),
notionVersion(),
],
}),
new CommandSpec({
name: 'edit',
description: "Edit a page's content from Markdown",
arguments: [content(), jsonOut(), notionVersion(), PAGE_ID],
}),
new CommandSpec({
name: 'trash',
description: 'Trash a page',
arguments: [
new Argument('--yes', { action: 'store_true', help: 'Skip the confirmation prompt' }),
notionVersion(),
PAGE_ID,
],
}),
],
}),
new CommandSpec({
name: 'whoami',
description: 'Show the authenticated Notion user',
arguments: [jsonOut(), plain(), notionVersion()],
}),
],
}),
handlers: {
api: new CLIHandler({ fn: guarded(api), write: true }),
'auth token': new CLIHandler({ fn: guarded(token) }),
'datasources query': new CLIHandler({ fn: guarded(query) }),
'datasources resolve': new CLIHandler({ fn: guarded(resolve) }),
'pages get': new CLIHandler({ fn: guarded(get) }),
'pages create': new CLIHandler({ fn: guarded(create), write: true }),
'pages edit': new CLIHandler({ fn: guarded(edit), write: true }),
'pages trash': new CLIHandler({ fn: guarded(trash), write: true }),
whoami: new CLIHandler({ fn: guarded(whoami) }),
},
configModel: NotionConfig,
accessor: ntnAccessor,
});
//# sourceMappingURL=index.js.map