@ssobig/writer-cli 0.2.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +35 -0
- package/asset-repository.js +278 -0
- package/config.js +14 -0
- package/package.json +28 -0
- package/project-runtime.js +102 -0
- package/storage-path.js +110 -0
- package/templates/mystery-v1/authoring-view-preference.js +34 -0
- package/templates/mystery-v1/character-perspective-preview.js +61 -0
- package/templates/mystery-v1/component-asset-operations.js +103 -0
- package/templates/mystery-v1/component-autosave.js +121 -0
- package/templates/mystery-v1/component-catalog-contract.js +340 -0
- package/templates/mystery-v1/component-checkpoint-history.js +145 -0
- package/templates/mystery-v1/component-contract.js +90 -0
- package/templates/mystery-v1/component-draft-operations.js +313 -0
- package/templates/mystery-v1/component-field-contracts.js +595 -0
- package/templates/mystery-v1/component-id-policy.js +64 -0
- package/templates/mystery-v1/component-manager.js +396 -0
- package/templates/mystery-v1/component-navigation-counts.js +64 -0
- package/templates/mystery-v1/component-registry.js +205 -0
- package/templates/mystery-v1/component-renderers.js +139 -0
- package/templates/mystery-v1/component-storage-contract.js +237 -0
- package/templates/mystery-v1/external-update-coordinator.js +91 -0
- package/templates/mystery-v1/output-clue-card-layout.js +46 -0
- package/templates/mystery-v1/page-header.js +26 -0
- package/templates/mystery-v1/render-ui-state.js +76 -0
- package/templates/mystery-v1/runtime-snapshot-reconciler.js +40 -0
- package/templates/mystery-v1/tab-bar.js +87 -0
- package/templates/mystery-v1/view-component-contract.js +152 -0
- package/templates/mystery-v1/view-component-registry.js +44 -0
- package/templates/mystery-v1/view-component-runtime.js +95 -0
- package/tools/writer-cli/bin/ssobig-writer-daemon.cjs +34 -0
- package/tools/writer-cli/bin/ssobig-writer.cjs +12 -0
- package/tools/writer-cli/package-lock.json +121 -0
- package/tools/writer-cli/package.json +22 -0
- package/tools/writer-cli/skills/ssobig-writer-cli/SKILL.md +38 -0
- package/tools/writer-cli/skills/ssobig-writer-cli/agents/openai.yaml +4 -0
- package/tools/writer-cli/skills/ssobig-writer-cli/references/assets-checkpoints.md +5 -0
- package/tools/writer-cli/skills/ssobig-writer-cli/references/errors.md +10 -0
- package/tools/writer-cli/skills/ssobig-writer-cli/references/install-auth.md +7 -0
- package/tools/writer-cli/skills/ssobig-writer-cli/references/projects-components.md +7 -0
- package/tools/writer-cli/skills/ssobig-writer-cli/references/read-search.md +5 -0
- package/tools/writer-cli/src/agent-paths.cjs +114 -0
- package/tools/writer-cli/src/agent-service.cjs +496 -0
- package/tools/writer-cli/src/asset-policy.cjs +113 -0
- package/tools/writer-cli/src/auth.cjs +655 -0
- package/tools/writer-cli/src/checkpoint-diff.cjs +128 -0
- package/tools/writer-cli/src/command-registry.cjs +152 -0
- package/tools/writer-cli/src/commands.cjs +841 -0
- package/tools/writer-cli/src/corpus.cjs +83 -0
- package/tools/writer-cli/src/daemon-app.cjs +106 -0
- package/tools/writer-cli/src/daemon-client.cjs +187 -0
- package/tools/writer-cli/src/daemon-protocol.cjs +184 -0
- package/tools/writer-cli/src/daemon-runner.cjs +97 -0
- package/tools/writer-cli/src/daemon-server.cjs +378 -0
- package/tools/writer-cli/src/diagnostics.cjs +235 -0
- package/tools/writer-cli/src/domain.cjs +731 -0
- package/tools/writer-cli/src/errors.cjs +47 -0
- package/tools/writer-cli/src/gateway.cjs +357 -0
- package/tools/writer-cli/src/investigation-board-layout.cjs +328 -0
- package/tools/writer-cli/src/json-patch.cjs +98 -0
- package/tools/writer-cli/src/json.cjs +26 -0
- package/tools/writer-cli/src/local-index-cache.cjs +139 -0
- package/tools/writer-cli/src/local-index-lookup.cjs +98 -0
- package/tools/writer-cli/src/local-index-query.cjs +304 -0
- package/tools/writer-cli/src/local-index-snapshot.cjs +235 -0
- package/tools/writer-cli/src/local-index-storage.cjs +284 -0
- package/tools/writer-cli/src/local-index.cjs +199 -0
- package/tools/writer-cli/src/mutations.cjs +722 -0
- package/tools/writer-cli/src/platform-runner.cjs +55 -0
- package/tools/writer-cli/src/project-import.cjs +485 -0
- package/tools/writer-cli/src/skill-manager.cjs +255 -0
- package/tools/writer-cli/src/source-fingerprint.cjs +90 -0
- package/tools/writer-cli/src/update-gate.cjs +102 -0
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
"use strict";
|
|
3
|
+
|
|
4
|
+
const { cliError, normalizeError, redact } = require("../src/errors.cjs");
|
|
5
|
+
|
|
6
|
+
function loadDaemonApp() {
|
|
7
|
+
try { return require("../src/daemon-app.cjs"); }
|
|
8
|
+
catch (error) {
|
|
9
|
+
throw cliError("E_DAEMON_APP_UNAVAILABLE", "Writer daemon application을 불러올 수 없습니다.", null, error);
|
|
10
|
+
}
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
async function main(options = {}) {
|
|
14
|
+
if (String(options.platform || process.platform) === "win32") {
|
|
15
|
+
throw cliError("E_PLATFORM_UNSUPPORTED", "Windows에서는 background daemon을 지원하지 않습니다. ssobig-writer의 in-process backend를 사용해 주세요.");
|
|
16
|
+
}
|
|
17
|
+
const argv = options.argv || process.argv.slice(2);
|
|
18
|
+
if (argv.some(argument => argument !== "--foreground") || argv.filter(argument => argument === "--foreground").length > 1) {
|
|
19
|
+
throw cliError("E_USAGE", "ssobig-writer-daemon은 --foreground option만 지원합니다.");
|
|
20
|
+
}
|
|
21
|
+
const app = options.app || loadDaemonApp();
|
|
22
|
+
if (typeof app?.runDaemon !== "function") throw cliError("E_DAEMON_APP_UNAVAILABLE", "Writer daemon application에 runDaemon()이 없습니다.");
|
|
23
|
+
return app.runDaemon({ foreground: true, enforceUpdates: true, ...(process.env.SSOBIG_WRITER_AGENT_CACHE_DIR ? { cacheDir: process.env.SSOBIG_WRITER_AGENT_CACHE_DIR } : {}) });
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
if (require.main === module) {
|
|
27
|
+
main().catch(error => {
|
|
28
|
+
const normalized = normalizeError(error);
|
|
29
|
+
process.stderr.write(`${JSON.stringify({ ok: false, error: { code: normalized.code, message: redact(normalized.message) } })}\n`);
|
|
30
|
+
process.exitCode = normalized.code === "E_USAGE" ? 2 : 1;
|
|
31
|
+
});
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
module.exports = Object.freeze({ loadDaemonApp, main });
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
"use strict";
|
|
3
|
+
|
|
4
|
+
const { runCli } = require("../src/commands.cjs");
|
|
5
|
+
const { redact } = require("../src/errors.cjs");
|
|
6
|
+
|
|
7
|
+
runCli(process.argv.slice(2), { enforceUpdates: true }).then(code => {
|
|
8
|
+
process.exitCode = code;
|
|
9
|
+
}).catch(error => {
|
|
10
|
+
process.stderr.write(`${JSON.stringify({ ok: false, error: { code: "E_WRITER_CLI_FATAL", message: redact(error?.message || error) } })}\n`);
|
|
11
|
+
process.exitCode = 1;
|
|
12
|
+
});
|
|
@@ -0,0 +1,121 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@ssobig/writer-cli",
|
|
3
|
+
"version": "0.2.0",
|
|
4
|
+
"lockfileVersion": 3,
|
|
5
|
+
"requires": true,
|
|
6
|
+
"packages": {
|
|
7
|
+
"": {
|
|
8
|
+
"name": "@ssobig/writer-cli",
|
|
9
|
+
"version": "0.2.0",
|
|
10
|
+
"dependencies": {
|
|
11
|
+
"@supabase/supabase-js": "2.110.9"
|
|
12
|
+
},
|
|
13
|
+
"bin": {
|
|
14
|
+
"ssobig-writer": "bin/ssobig-writer.cjs",
|
|
15
|
+
"ssobig-writer-daemon": "bin/ssobig-writer-daemon.cjs"
|
|
16
|
+
},
|
|
17
|
+
"engines": {
|
|
18
|
+
"node": ">=22.13.0"
|
|
19
|
+
}
|
|
20
|
+
},
|
|
21
|
+
"node_modules/@supabase/auth-js": {
|
|
22
|
+
"version": "2.110.9",
|
|
23
|
+
"resolved": "https://registry.npmjs.org/@supabase/auth-js/-/auth-js-2.110.9.tgz",
|
|
24
|
+
"integrity": "sha512-XQIWyJfwEUdtzinuLUWPNSJbJKWdCQPqC7dDY45lA1qhX/OzvGPE+4OyPPZVOVWVh4DlPvk5RT2XL6SEAY0MoA==",
|
|
25
|
+
"license": "MIT",
|
|
26
|
+
"dependencies": {
|
|
27
|
+
"tslib": "2.8.1"
|
|
28
|
+
},
|
|
29
|
+
"engines": {
|
|
30
|
+
"node": ">=22.0.0"
|
|
31
|
+
}
|
|
32
|
+
},
|
|
33
|
+
"node_modules/@supabase/functions-js": {
|
|
34
|
+
"version": "2.110.9",
|
|
35
|
+
"resolved": "https://registry.npmjs.org/@supabase/functions-js/-/functions-js-2.110.9.tgz",
|
|
36
|
+
"integrity": "sha512-iSL6ih4vWK5Myc1lCuDoApfeB3Ov3cZIExvlCuQEZTzyzOKG4SLxVKPrBZG/QzwAjp7TEy0LCd0zZQtE5UAaDQ==",
|
|
37
|
+
"license": "MIT",
|
|
38
|
+
"dependencies": {
|
|
39
|
+
"tslib": "2.8.1"
|
|
40
|
+
},
|
|
41
|
+
"engines": {
|
|
42
|
+
"node": ">=22.0.0"
|
|
43
|
+
}
|
|
44
|
+
},
|
|
45
|
+
"node_modules/@supabase/phoenix": {
|
|
46
|
+
"version": "0.4.5",
|
|
47
|
+
"resolved": "https://registry.npmjs.org/@supabase/phoenix/-/phoenix-0.4.5.tgz",
|
|
48
|
+
"integrity": "sha512-aAn9H9ovVyeApKy11OWOrrOGq8DV68yWeH4ud2lN9fzn4aO8Zb5GLL9m1pUg9nLqIcT+ZDfAcsZe0E/nqdv2lw==",
|
|
49
|
+
"license": "MIT"
|
|
50
|
+
},
|
|
51
|
+
"node_modules/@supabase/postgrest-js": {
|
|
52
|
+
"version": "2.110.9",
|
|
53
|
+
"resolved": "https://registry.npmjs.org/@supabase/postgrest-js/-/postgrest-js-2.110.9.tgz",
|
|
54
|
+
"integrity": "sha512-YEfUzBMNkDE9OMfjzZnVgJH9h7y+wxxXryrgYk/t3boVtjPPlfwTdWRx8YkihuIbl4zxzdG4I0yiS4nd4Lmj8Q==",
|
|
55
|
+
"license": "MIT",
|
|
56
|
+
"dependencies": {
|
|
57
|
+
"tslib": "2.8.1"
|
|
58
|
+
},
|
|
59
|
+
"engines": {
|
|
60
|
+
"node": ">=22.0.0"
|
|
61
|
+
}
|
|
62
|
+
},
|
|
63
|
+
"node_modules/@supabase/realtime-js": {
|
|
64
|
+
"version": "2.110.9",
|
|
65
|
+
"resolved": "https://registry.npmjs.org/@supabase/realtime-js/-/realtime-js-2.110.9.tgz",
|
|
66
|
+
"integrity": "sha512-XHau+5jIOEAtS9hNiSrL4/2SXSt0EApChsj+f2VJL2mL+eicpUYLkANBu+5awFLIjXGOkys+ClYQ7Dg/3UTHcA==",
|
|
67
|
+
"license": "MIT",
|
|
68
|
+
"dependencies": {
|
|
69
|
+
"@supabase/phoenix": "0.4.5",
|
|
70
|
+
"tslib": "2.8.1"
|
|
71
|
+
},
|
|
72
|
+
"engines": {
|
|
73
|
+
"node": ">=22.0.0"
|
|
74
|
+
}
|
|
75
|
+
},
|
|
76
|
+
"node_modules/@supabase/storage-js": {
|
|
77
|
+
"version": "2.110.9",
|
|
78
|
+
"resolved": "https://registry.npmjs.org/@supabase/storage-js/-/storage-js-2.110.9.tgz",
|
|
79
|
+
"integrity": "sha512-UmyL+SB4cOBsejAaJ8LnLIm31585shJtnTezCCLMHyo2AauoH13sKJqPllf/8HMcGYqd0a4kHrA5t2nz+MSARA==",
|
|
80
|
+
"license": "MIT",
|
|
81
|
+
"dependencies": {
|
|
82
|
+
"iceberg-js": "^0.8.1",
|
|
83
|
+
"tslib": "2.8.1"
|
|
84
|
+
},
|
|
85
|
+
"engines": {
|
|
86
|
+
"node": ">=22.0.0"
|
|
87
|
+
}
|
|
88
|
+
},
|
|
89
|
+
"node_modules/@supabase/supabase-js": {
|
|
90
|
+
"version": "2.110.9",
|
|
91
|
+
"resolved": "https://registry.npmjs.org/@supabase/supabase-js/-/supabase-js-2.110.9.tgz",
|
|
92
|
+
"integrity": "sha512-C8UVe+x6hymwiLdJntQCm9zMD5+40tWF6j+17Z6g8NJnBAWEiuM9wXYAqgDzGAQF6oqrkHddmnZwWZZtEp0zeA==",
|
|
93
|
+
"license": "MIT",
|
|
94
|
+
"dependencies": {
|
|
95
|
+
"@supabase/auth-js": "2.110.9",
|
|
96
|
+
"@supabase/functions-js": "2.110.9",
|
|
97
|
+
"@supabase/postgrest-js": "2.110.9",
|
|
98
|
+
"@supabase/realtime-js": "2.110.9",
|
|
99
|
+
"@supabase/storage-js": "2.110.9"
|
|
100
|
+
},
|
|
101
|
+
"engines": {
|
|
102
|
+
"node": ">=22.0.0"
|
|
103
|
+
}
|
|
104
|
+
},
|
|
105
|
+
"node_modules/iceberg-js": {
|
|
106
|
+
"version": "0.8.1",
|
|
107
|
+
"resolved": "https://registry.npmjs.org/iceberg-js/-/iceberg-js-0.8.1.tgz",
|
|
108
|
+
"integrity": "sha512-1dhVQZXhcHje7798IVM+xoo/1ZdVfzOMIc8/rgVSijRK38EDqOJoGula9N/8ZI5RD8QTxNQtK/Gozpr+qUqRRA==",
|
|
109
|
+
"license": "MIT",
|
|
110
|
+
"engines": {
|
|
111
|
+
"node": ">=20.0.0"
|
|
112
|
+
}
|
|
113
|
+
},
|
|
114
|
+
"node_modules/tslib": {
|
|
115
|
+
"version": "2.8.1",
|
|
116
|
+
"resolved": "https://registry.npmjs.org/tslib/-/tslib-2.8.1.tgz",
|
|
117
|
+
"integrity": "sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w==",
|
|
118
|
+
"license": "0BSD"
|
|
119
|
+
}
|
|
120
|
+
}
|
|
121
|
+
}
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@ssobig/writer-cli",
|
|
3
|
+
"version": "0.2.0",
|
|
4
|
+
"private": true,
|
|
5
|
+
"description": "Internal agent-safe CLI for SSOBIG WRITER manuscript and asset operations",
|
|
6
|
+
"type": "commonjs",
|
|
7
|
+
"bin": {
|
|
8
|
+
"ssobig-writer": "bin/ssobig-writer.cjs",
|
|
9
|
+
"ssobig-writer-daemon": "bin/ssobig-writer-daemon.cjs"
|
|
10
|
+
},
|
|
11
|
+
"engines": {
|
|
12
|
+
"node": ">=22.13.0"
|
|
13
|
+
},
|
|
14
|
+
"scripts": {
|
|
15
|
+
"build:public": "node scripts/build-public-package.cjs",
|
|
16
|
+
"verify:public": "node scripts/verify-public-package.cjs",
|
|
17
|
+
"test": "node --test ../../tests/writer-cli-*.test.cjs"
|
|
18
|
+
},
|
|
19
|
+
"dependencies": {
|
|
20
|
+
"@supabase/supabase-js": "2.110.9"
|
|
21
|
+
}
|
|
22
|
+
}
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: ssobig-writer-cli
|
|
3
|
+
description: Safely discover and use the public SSOBIG WRITER CLI for manuscript, Component, asset, checkpoint, and project operations.
|
|
4
|
+
---
|
|
5
|
+
|
|
6
|
+
# SSOBIG WRITER CLI
|
|
7
|
+
|
|
8
|
+
Use this skill when a task asks you to inspect or change a Writer project through `ssobig-writer`. Treat the CLI, not this document, as the source of truth for command syntax. Start with the narrowest discovery call that answers the question:
|
|
9
|
+
|
|
10
|
+
```bash
|
|
11
|
+
ssobig-writer <group> --help
|
|
12
|
+
ssobig-writer <group> <command> --help
|
|
13
|
+
ssobig-writer command-schema <group.command> --compact
|
|
14
|
+
```
|
|
15
|
+
|
|
16
|
+
Do not dump the complete schema when one group or leaf is enough. The root schema intentionally returns only group summaries; a group returns leaf summaries; an exact leaf returns full inputs, examples, and safety metadata.
|
|
17
|
+
|
|
18
|
+
Route by intent and read only the relevant reference:
|
|
19
|
+
|
|
20
|
+
- Installation, sign-in, diagnostics, updates, or project-local skill setup: `references/install-auth.md`
|
|
21
|
+
- Finding manuscript values or reading targeted Component data: `references/read-search.md`
|
|
22
|
+
- Project identity, versions, Component reads, and reviewed data plans: `references/projects-components.md`
|
|
23
|
+
- Asset pointers, checkpoint history, restore plans, and receipts: `references/assets-checkpoints.md`
|
|
24
|
+
- Any failure, conflict, authorization issue, or boundary response: `references/errors.md`
|
|
25
|
+
|
|
26
|
+
The authoritative manuscript source is Writer Component data. Never bypass the CLI with raw SQL, generic database updates, a Supabase management client, or a service-role credential. Never place manuscript values in source code, View HTML/JavaScript, local caches, or ad-hoc JSON as a substitute for Component data.
|
|
27
|
+
|
|
28
|
+
For every production mutation, follow this sequence without shortcuts:
|
|
29
|
+
|
|
30
|
+
1. Read the authoritative current state.
|
|
31
|
+
2. Create a plan with the dedicated `plan-*` command.
|
|
32
|
+
3. Present a human-readable review of the exact target and changes.
|
|
33
|
+
4. Run `apply` only after the user explicitly authorizes that mutation.
|
|
34
|
+
5. Read the authoritative state again and retain the receipt privately.
|
|
35
|
+
|
|
36
|
+
A general request to inspect code or explain behavior does not authorize manuscript mutation. Plan and receipt files can contain confidential content; store them in a private ignored directory and never commit them.
|
|
37
|
+
|
|
38
|
+
`E_CODE_CHANGE_REQUIRED` is a hard stop. It means the requested operation changes shared schemas, Component composition, Renderer/View behavior, identity, or another boundary outside the content CLI. Do not work around it. Report the boundary and separate the request into a reviewed code or migration task.
|
|
@@ -0,0 +1,5 @@
|
|
|
1
|
+
# Assets and checkpoints
|
|
2
|
+
|
|
3
|
+
Asset pointers are owned by the dedicated asset Component. Use only `asset plan-upload` or `asset plan-delete`; do not patch pointers through generic Component data. Validate the selected project, asset ID, file type, and expected prior entry before review and apply.
|
|
4
|
+
|
|
5
|
+
Checkpoint commands provide history, snapshots, diffs, creation plans, and restore plans. A restore is a production mutation and follows plan, review, explicit authorization, apply, authoritative read-back. Keep plan and receipt files private. Use `checkpoint diff` to make the review understandable before a restore.
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
# Errors and boundaries
|
|
2
|
+
|
|
3
|
+
- `E_CLI_UPDATE_REQUIRED`: install the indicated latest npm package, then retry. The original operation was not sent.
|
|
4
|
+
- `E_CODE_CHANGE_REQUIRED`: hard boundary; do not use SQL or another client. Separate a code or migration task.
|
|
5
|
+
- `E_CONFLICT`: authoritative data changed after the plan. Read again and create a new plan.
|
|
6
|
+
- `E_AUTH_REQUIRED`, `E_AUTH_EXPIRED`, `E_AUTHORIZATION`: use the supported login flow or ask an authorized staff member; never handle tokens directly.
|
|
7
|
+
- `E_SKILL_CONFLICT`: an unmanaged or user-modified skill file is present. Preserve it and inspect status/diff; do not overwrite it.
|
|
8
|
+
- Validation and usage errors: inspect the exact leaf help/schema and correct the request. Do not coerce unknown templates, renderers, bindings, identities, or schemas.
|
|
9
|
+
|
|
10
|
+
Registry-unavailable update warnings are sanitized and non-blocking unless the local cache already records a newer required version.
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
# Install, authentication, and diagnostics
|
|
2
|
+
|
|
3
|
+
Use targeted help for `version`, `doctor`, `auth`, and `skills`. Install the npm package explicitly; there is no `postinstall` workspace mutation. Run `ssobig-writer skills install --target <workspace>` to install the bundled skill in both supported agent directories, or select one platform with `--codex` or `--claude`.
|
|
4
|
+
|
|
5
|
+
Check `version --check` before long work. From CLI 0.2.0 onward, outdated server-access commands stop with `E_CLI_UPDATE_REQUIRED`; local help, schemas, diagnostics, logout, daemon cleanup, and skill management remain available. Registry outages fail open unless a cached newer version is already known.
|
|
6
|
+
|
|
7
|
+
Authentication uses the supported staff flow. Never request, print, persist, or transmit access tokens. Use `doctor` for sanitized installation, credential-store, network, update, and daemon-version diagnostics.
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
# Projects and Components
|
|
2
|
+
|
|
3
|
+
Project commands expose identity, versions, and reviewed project-level plans. Component commands are the supported path for authoritative manuscript values. Read first, preserve the selected project/version/Instance identity, and use a dedicated `plan-*` command for changes.
|
|
4
|
+
|
|
5
|
+
Generic Component patching changes only an existing Instance's `data`. It cannot change Component composition, metadata, View bindings, templates, schemas, project identity, or storage paths. If the CLI returns `E_CODE_CHANGE_REQUIRED`, stop and report that a separate code or migration review is required.
|
|
6
|
+
|
|
7
|
+
Never apply a generated plan until the user has reviewed a human-readable summary and explicitly authorized the production mutation. Read back the same Component after apply.
|
|
@@ -0,0 +1,5 @@
|
|
|
1
|
+
# Read and search
|
|
2
|
+
|
|
3
|
+
Start with `search` when the target location is unknown, then use the returned stable identifier with `read`, `component get`, or `agent get`. Prefer exact project, Component, path, and value filters. Use `--fresh` when authoritative refresh is desirable and `--require-fresh` when stale indexed data is unacceptable.
|
|
4
|
+
|
|
5
|
+
Avoid broad corpus dumps. Inspect exact syntax through `search --help`, `read --help`, or the exact leaf schema. Search and read output may contain confidential manuscript content; summarize only what the task needs.
|
|
@@ -0,0 +1,114 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
|
|
3
|
+
const crypto = require("node:crypto");
|
|
4
|
+
const fs = require("node:fs");
|
|
5
|
+
const os = require("node:os");
|
|
6
|
+
const path = require("node:path");
|
|
7
|
+
const config = require("../../../config.js");
|
|
8
|
+
const { cliError } = require("./errors.cjs");
|
|
9
|
+
|
|
10
|
+
function projectRef() {
|
|
11
|
+
try { return new URL(config.supabaseUrl).hostname.split(".")[0]; }
|
|
12
|
+
catch (error) { throw cliError("E_CONFIGURATION", "Supabase project ref를 확인할 수 없습니다.", null, error); }
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
function localAppData(options = {}) {
|
|
16
|
+
const environment = options.environment || process.env;
|
|
17
|
+
const homeDirectory = options.homeDirectory || os.homedir();
|
|
18
|
+
return environment.LOCALAPPDATA || path.join(homeDirectory, "AppData", "Local");
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
function defaultAgentCacheDir(options = {}) {
|
|
22
|
+
const platform = String(options.platform || process.platform);
|
|
23
|
+
if (platform === "win32") return path.join(localAppData(options), "SSOBIG", "WriterCLI", "cache", "v1", projectRef());
|
|
24
|
+
return path.join(options.homeDirectory || os.homedir(), "Library", "Caches", "com.ssobig.writer-agent", "v1", projectRef());
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
function defaultDaemonDirectory(options = {}) {
|
|
28
|
+
const platform = String(options.platform || process.platform);
|
|
29
|
+
if (platform === "win32") return path.join(localAppData(options), "SSOBIG", "WriterCLI", "daemon", projectRef());
|
|
30
|
+
return path.join(options.homeDirectory || os.homedir(), "Library", "Caches", "ssobig-wa", projectRef());
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
function resolveAgentPaths(options = {}) {
|
|
34
|
+
const platform = String(options.platform || process.platform);
|
|
35
|
+
const defaults = { platform, environment: options.environment, homeDirectory: options.homeDirectory };
|
|
36
|
+
const cacheDir = path.resolve(String(options.cacheDir || defaultAgentCacheDir(defaults)));
|
|
37
|
+
const daemonDir = path.resolve(String(options.daemonDir || (options.cacheDir ? cacheDir : defaultDaemonDirectory(defaults))));
|
|
38
|
+
const identity = String(options.userId || "");
|
|
39
|
+
const userKey = identity ? crypto.createHash("sha256").update(identity).digest("hex").slice(0, 32) : "";
|
|
40
|
+
return Object.freeze({
|
|
41
|
+
cacheDir,
|
|
42
|
+
daemonDir,
|
|
43
|
+
socketPath: platform === "win32" ? null : path.join(daemonDir, "d.sock"),
|
|
44
|
+
pidPath: path.join(daemonDir, "daemon.pid"),
|
|
45
|
+
...(userKey ? {
|
|
46
|
+
userCacheDir: path.join(cacheDir, "users", userKey),
|
|
47
|
+
indexPath: path.join(cacheDir, "users", userKey, "manuscript-index.sqlite3")
|
|
48
|
+
} : {})
|
|
49
|
+
});
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
function ensurePrivateDirectory(directory, fsApi = fs, options = {}) {
|
|
53
|
+
const platform = String(options.platform || process.platform);
|
|
54
|
+
try {
|
|
55
|
+
if (fsApi.lstatSync(directory).isSymbolicLink()) throw cliError("E_CACHE_PERMISSIONS", `Writer agent cache는 symbolic link일 수 없습니다: ${directory}`);
|
|
56
|
+
} catch (error) { if (error.code !== "ENOENT") throw error; }
|
|
57
|
+
fsApi.mkdirSync(directory, { recursive: true, mode: 0o700 });
|
|
58
|
+
const initial = fsApi.lstatSync(directory);
|
|
59
|
+
if (initial.isSymbolicLink() || !initial.isDirectory()) throw cliError("E_CACHE_PERMISSIONS", `Writer agent cache는 symbolic link가 아닌 directory여야 합니다: ${directory}`);
|
|
60
|
+
if (platform === "win32") return directory;
|
|
61
|
+
if (typeof process.getuid === "function" && initial.uid !== process.getuid()) {
|
|
62
|
+
throw cliError("E_CACHE_PERMISSIONS", `Writer agent cache의 소유자가 현재 사용자와 다릅니다: ${directory}`);
|
|
63
|
+
}
|
|
64
|
+
try { fsApi.chmodSync(directory, 0o700); }
|
|
65
|
+
catch (error) { throw cliError("E_CACHE_PERMISSIONS", `Writer agent cache 권한을 설정할 수 없습니다: ${directory}`, null, error); }
|
|
66
|
+
const stat = fsApi.statSync(directory);
|
|
67
|
+
if (!stat.isDirectory() || (stat.mode & 0o077) !== 0) {
|
|
68
|
+
throw cliError("E_CACHE_PERMISSIONS", `Writer agent cache는 현재 사용자만 접근할 수 있어야 합니다: ${directory}`);
|
|
69
|
+
}
|
|
70
|
+
return directory;
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
function purgeAllAgentIndexes(options = {}) {
|
|
74
|
+
const fsApi = options.fs || fs;
|
|
75
|
+
const platform = String(options.platform || process.platform);
|
|
76
|
+
const cacheDir = resolveAgentPaths({
|
|
77
|
+
cacheDir: options.cacheDir,
|
|
78
|
+
platform,
|
|
79
|
+
environment: options.environment,
|
|
80
|
+
homeDirectory: options.homeDirectory
|
|
81
|
+
}).cacheDir;
|
|
82
|
+
const usersDir = path.join(cacheDir, "users");
|
|
83
|
+
let userEntries;
|
|
84
|
+
try {
|
|
85
|
+
const usersStat = fsApi.lstatSync(usersDir);
|
|
86
|
+
if (usersStat.isSymbolicLink() || !usersStat.isDirectory()
|
|
87
|
+
|| (platform !== "win32" && typeof process.getuid === "function" && usersStat.uid !== process.getuid())) {
|
|
88
|
+
throw cliError("E_CACHE_PERMISSIONS", "Writer agent users cache가 안전한 private directory가 아닙니다.");
|
|
89
|
+
}
|
|
90
|
+
userEntries = fsApi.readdirSync(usersDir, { withFileTypes: true });
|
|
91
|
+
}
|
|
92
|
+
catch (error) { if (error.code === "ENOENT") return { purged: true, removedFiles: 0 }; throw error; }
|
|
93
|
+
const allowedFiles = new Set([
|
|
94
|
+
"manuscript-index.sqlite3", "manuscript-index.sqlite3-journal", "manuscript-index.sqlite3-wal", "manuscript-index.sqlite3-shm",
|
|
95
|
+
"manuscript-index.json", "index.sqlite3", "index.sqlite3-journal", "index.sqlite3-wal", "index.sqlite3-shm", "index.json"
|
|
96
|
+
]);
|
|
97
|
+
let removedFiles = 0;
|
|
98
|
+
for (const entry of userEntries) {
|
|
99
|
+
if (!/^[a-f0-9]{32}$/.test(entry.name) || !entry.isDirectory() || entry.isSymbolicLink()) continue;
|
|
100
|
+
const userDir = path.join(usersDir, entry.name);
|
|
101
|
+
const stat = fsApi.lstatSync(userDir);
|
|
102
|
+
if (platform !== "win32" && typeof process.getuid === "function" && stat.uid !== process.getuid()) continue;
|
|
103
|
+
for (const child of fsApi.readdirSync(userDir, { withFileTypes: true })) {
|
|
104
|
+
if (!allowedFiles.has(child.name) || !child.isFile() || child.isSymbolicLink()) continue;
|
|
105
|
+
fsApi.unlinkSync(path.join(userDir, child.name));
|
|
106
|
+
removedFiles += 1;
|
|
107
|
+
}
|
|
108
|
+
try { fsApi.rmdirSync(userDir); } catch (error) { if (!["ENOTEMPTY", "ENOENT"].includes(error.code)) throw error; }
|
|
109
|
+
}
|
|
110
|
+
try { fsApi.rmdirSync(usersDir); } catch (error) { if (!["ENOTEMPTY", "ENOENT"].includes(error.code)) throw error; }
|
|
111
|
+
return { purged: true, removedFiles };
|
|
112
|
+
}
|
|
113
|
+
|
|
114
|
+
module.exports = Object.freeze({ defaultAgentCacheDir, defaultDaemonDirectory, resolveAgentPaths, ensurePrivateDirectory, purgeAllAgentIndexes });
|