@agent-native/core 0.131.6 → 0.131.9
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/corpus/README.md +1 -1
- package/corpus/core/CHANGELOG.md +31 -0
- package/corpus/core/docs/content/deployment.mdx +12 -10
- package/corpus/core/docs/content/security.mdx +2 -2
- package/corpus/core/package.json +1 -1
- package/corpus/core/src/a2a/invoke.ts +4 -2
- package/corpus/core/src/agent/action-continuation-guidance.ts +2 -1
- package/corpus/core/src/agent/engine/registry.ts +61 -9
- package/corpus/core/src/application-state/store.ts +18 -31
- package/corpus/core/src/cli/create.ts +115 -16
- package/corpus/core/src/client/AssistantChat.tsx +10 -2
- package/corpus/core/src/client/frame-protocol.ts +5 -1
- package/corpus/core/src/guards/no-env-credentials.ts +2 -0
- package/corpus/core/src/scripts/call-agent.ts +4 -2
- package/corpus/core/src/secrets/crypto.ts +44 -15
- package/corpus/core/src/secrets/storage.ts +6 -4
- package/corpus/core/src/server/agent-chat/action-filters-a2a.ts +2 -0
- package/corpus/core/src/templates/default/.agents/skills/secrets/SKILL.md +14 -9
- package/corpus/core/src/templates/headless/.agents/skills/secrets/SKILL.md +14 -9
- package/corpus/core/src/templates/workspace-core/.agents/skills/secrets/SKILL.md +14 -9
- package/corpus/core/src/workspace-files/tool.ts +18 -11
- package/corpus/templates/analytics/AGENTS.md +1 -0
- package/corpus/templates/analytics/changelog/2026-07-30-delegated-analytics-questions-now-return-a-useful-current-me.md +6 -0
- package/corpus/templates/analytics/server/lib/real-data-actions.ts +10 -0
- package/corpus/templates/clips/actions/generate-workflow.ts +6 -4
- package/corpus/templates/clips/actions/reconcile-workflow-generation.ts +257 -0
- package/corpus/templates/clips/app/hooks/use-auto-title.ts +166 -4
- package/corpus/templates/clips/app/routes/r.$recordingId.tsx +1 -1
- package/corpus/templates/clips/changelog/2026-07-27-stopped-agent-runs-no-longer-leave-generated-workflow-cards-.md +6 -0
- package/corpus/templates/clips/changelog/2026-07-30-workflow-generation-now-stops-retrying-after-repeated-failur.md +6 -0
- package/corpus/templates/clips/shared/workflow.ts +5 -0
- package/corpus/templates/plan/actions/get-visual-plan.ts +10 -6
- package/corpus/templates/plan/actions/patch-visual-plan-source.ts +117 -1
- package/corpus/templates/plan/actions/update-visual-plan.ts +56 -14
- package/corpus/templates/plan/app/pages/PlansPage.tsx +1 -1
- package/corpus/templates/plan/changelog/2026-07-30-plan-agents-recover-with-targeted-writes.md +6 -0
- package/corpus/templates/plan/shared/plan-content.ts +35 -0
- package/corpus/templates/slides/actions/create-deck.ts +1 -1
- package/corpus/templates/slides/changelog/2026-07-30-new-decks-now-start-with-an-empty-slide-list-when-no-slides-.md +6 -0
- package/dist/a2a/invoke.d.ts.map +1 -1
- package/dist/a2a/invoke.js +4 -2
- package/dist/a2a/invoke.js.map +1 -1
- package/dist/agent/action-continuation-guidance.d.ts.map +1 -1
- package/dist/agent/action-continuation-guidance.js +2 -1
- package/dist/agent/action-continuation-guidance.js.map +1 -1
- package/dist/agent/engine/registry.d.ts.map +1 -1
- package/dist/agent/engine/registry.js +59 -12
- package/dist/agent/engine/registry.js.map +1 -1
- package/dist/application-state/store.d.ts.map +1 -1
- package/dist/application-state/store.js +20 -36
- package/dist/application-state/store.js.map +1 -1
- package/dist/cli/create.d.ts +8 -1
- package/dist/cli/create.d.ts.map +1 -1
- package/dist/cli/create.js +98 -16
- package/dist/cli/create.js.map +1 -1
- package/dist/client/AssistantChat.d.ts.map +1 -1
- package/dist/client/AssistantChat.js +10 -2
- package/dist/client/AssistantChat.js.map +1 -1
- package/dist/client/frame-protocol.d.ts +1 -0
- package/dist/client/frame-protocol.d.ts.map +1 -1
- package/dist/client/frame-protocol.js.map +1 -1
- package/dist/collab/routes.d.ts +1 -1
- package/dist/collab/struct-routes.d.ts +1 -1
- package/dist/file-upload/actions/upload-image.d.ts +1 -1
- package/dist/guards/no-env-credentials.d.ts.map +1 -1
- package/dist/guards/no-env-credentials.js +2 -0
- package/dist/guards/no-env-credentials.js.map +1 -1
- package/dist/mcp/screen-memory-stdio.d.ts +7 -7
- package/dist/mcp/screen-memory-stdio.d.ts.map +1 -1
- package/dist/notifications/routes.d.ts +6 -6
- package/dist/observability/routes.d.ts +3 -3
- package/dist/scripts/call-agent.d.ts.map +1 -1
- package/dist/scripts/call-agent.js +4 -2
- package/dist/scripts/call-agent.js.map +1 -1
- package/dist/secrets/crypto.d.ts +14 -9
- package/dist/secrets/crypto.d.ts.map +1 -1
- package/dist/secrets/crypto.js +42 -16
- package/dist/secrets/crypto.js.map +1 -1
- package/dist/secrets/storage.d.ts +6 -4
- package/dist/secrets/storage.d.ts.map +1 -1
- package/dist/secrets/storage.js +6 -4
- package/dist/secrets/storage.js.map +1 -1
- package/dist/server/agent-chat/action-filters-a2a.d.ts +1 -1
- package/dist/server/agent-chat/action-filters-a2a.d.ts.map +1 -1
- package/dist/server/agent-chat/action-filters-a2a.js +2 -0
- package/dist/server/agent-chat/action-filters-a2a.js.map +1 -1
- package/dist/server/realtime-token.d.ts +1 -1
- package/dist/server/transcribe-voice.d.ts +1 -1
- package/dist/templates/default/.agents/skills/secrets/SKILL.md +14 -9
- package/dist/templates/headless/.agents/skills/secrets/SKILL.md +14 -9
- package/dist/templates/workspace-core/.agents/skills/secrets/SKILL.md +14 -9
- package/dist/workspace-files/tool.d.ts.map +1 -1
- package/dist/workspace-files/tool.js +18 -11
- package/dist/workspace-files/tool.js.map +1 -1
- package/docs/content/deployment.mdx +12 -10
- package/docs/content/security.mdx +2 -2
- package/package.json +1 -1
- package/src/a2a/invoke.ts +4 -2
- package/src/agent/action-continuation-guidance.ts +2 -1
- package/src/agent/engine/registry.ts +61 -9
- package/src/application-state/store.ts +18 -31
- package/src/cli/create.ts +115 -16
- package/src/client/AssistantChat.tsx +10 -2
- package/src/client/frame-protocol.ts +5 -1
- package/src/guards/no-env-credentials.ts +2 -0
- package/src/scripts/call-agent.ts +4 -2
- package/src/secrets/crypto.ts +44 -15
- package/src/secrets/storage.ts +6 -4
- package/src/server/agent-chat/action-filters-a2a.ts +2 -0
- package/src/templates/default/.agents/skills/secrets/SKILL.md +14 -9
- package/src/templates/headless/.agents/skills/secrets/SKILL.md +14 -9
- package/src/templates/workspace-core/.agents/skills/secrets/SKILL.md +14 -9
- package/src/workspace-files/tool.ts +18 -11
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { getDbExec, getDialect, isLocalDatabase,
|
|
1
|
+
import { getDbExec, getDialect, isLocalDatabase, isPostgres, intType, } from "../db/client.js";
|
|
2
2
|
import { ensureIndexExists, ensureTableExists } from "../db/ddl-guard.js";
|
|
3
3
|
import { widenIntColumnsToBigInt } from "../db/widen-columns.js";
|
|
4
4
|
import { emitAppStateChange, emitAppStateDelete } from "./emitter.js";
|
|
@@ -79,24 +79,15 @@ async function ensureTable() {
|
|
|
79
79
|
return _initPromise;
|
|
80
80
|
}
|
|
81
81
|
export async function appStateGet(sessionId, key) {
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
return JSON.parse(rows[0].value);
|
|
92
|
-
}
|
|
93
|
-
catch (err) {
|
|
94
|
-
// Transient WS / connection drops (Neon serverless) — caller polls every
|
|
95
|
-
// 2s and will see the value on the next tick. Swallow rather than 500.
|
|
96
|
-
if (isConnectionError(err))
|
|
97
|
-
return null;
|
|
98
|
-
throw err;
|
|
99
|
-
}
|
|
82
|
+
await ensureTable();
|
|
83
|
+
const client = getDbExec();
|
|
84
|
+
const { rows } = await client.execute({
|
|
85
|
+
sql: `SELECT value FROM application_state WHERE session_id = ? AND key = ?`,
|
|
86
|
+
args: [sessionId, key],
|
|
87
|
+
});
|
|
88
|
+
if (rows.length === 0)
|
|
89
|
+
return null;
|
|
90
|
+
return JSON.parse(rows[0].value);
|
|
100
91
|
}
|
|
101
92
|
/**
|
|
102
93
|
* Read several application-state keys for one session in a single SQL query.
|
|
@@ -110,24 +101,17 @@ export async function appStateGetMany(sessionId, keys) {
|
|
|
110
101
|
values[key] = null;
|
|
111
102
|
if (uniqueKeys.length === 0)
|
|
112
103
|
return values;
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
values[row.key] = JSON.parse(row.value);
|
|
123
|
-
}
|
|
124
|
-
return values;
|
|
125
|
-
}
|
|
126
|
-
catch (err) {
|
|
127
|
-
if (isConnectionError(err))
|
|
128
|
-
return values;
|
|
129
|
-
throw err;
|
|
104
|
+
await ensureTable();
|
|
105
|
+
const client = getDbExec();
|
|
106
|
+
const placeholders = uniqueKeys.map(() => "?").join(", ");
|
|
107
|
+
const { rows } = await client.execute({
|
|
108
|
+
sql: `SELECT key, value FROM application_state WHERE session_id = ? AND key IN (${placeholders})`,
|
|
109
|
+
args: [sessionId, ...uniqueKeys],
|
|
110
|
+
});
|
|
111
|
+
for (const row of rows) {
|
|
112
|
+
values[row.key] = JSON.parse(row.value);
|
|
130
113
|
}
|
|
114
|
+
return values;
|
|
131
115
|
}
|
|
132
116
|
export async function appStatePut(sessionId, key, value, options) {
|
|
133
117
|
await ensureTable();
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"store.js","sourceRoot":"","sources":["../../src/application-state/store.ts"],"names":[],"mappings":"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","sourcesContent":["import {\n getDbExec,\n getDialect,\n isLocalDatabase,\n isConnectionError,\n isPostgres,\n intType,\n type DbExec,\n} from \"../db/client.js\";\nimport { ensureIndexExists, ensureTableExists } from \"../db/ddl-guard.js\";\nimport { widenIntColumnsToBigInt } from \"../db/widen-columns.js\";\nimport type { StoreWriteOptions } from \"../settings/store.js\";\nimport { emitAppStateChange, emitAppStateDelete } from \"./emitter.js\";\n\nlet _initPromise: Promise<void> | undefined;\nconst MAX_HOSTED_APP_STATE_VALUE_BYTES = 1024 * 1024;\n\n// Escapes LIKE wildcards (`%`, `_`) and the escape char itself so a caller's\n// literal prefix is matched verbatim. Used with `ESCAPE '!'` in prefix queries\n// below; without this, a prefix such as `user_settings` would treat `_` as a\n// single-char wildcard and over-match (e.g. delete `userXsettings`).\nfunction escapeLike(s: string): string {\n return s.replace(/[!%_]/g, (match) => `!${match}`);\n}\n\nasync function ensureTable(): Promise<void> {\n if (!_initPromise) {\n _initPromise = (async () => {\n const client = getDbExec();\n const createSql = `\n CREATE TABLE IF NOT EXISTS application_state (\n session_id TEXT NOT NULL,\n key TEXT NOT NULL,\n value TEXT NOT NULL,\n updated_at ${intType()} NOT NULL,\n PRIMARY KEY (session_id, key)\n )\n `;\n\n if (isPostgres()) {\n // Hot path: the `application_state` table and its poll indexes are\n // virtually always already present in production. Issuing `CREATE\n // TABLE`/ `CREATE INDEX` still takes a lock that, in a fresh\n // background-worker process behind a concurrent connection on the\n // shared Neon DB, can block ~indefinitely (ACCESS EXCLUSIVE for CREATE\n // TABLE; a write-blocking SHARE lock for CREATE INDEX). The ensure*\n // wrappers probe `information_schema`/`pg_indexes` first (plain reads,\n // no lock) and run DDL ONLY for what is actually missing, bounded by a\n // transaction-scoped `lock_timeout`. If a swallowed lock-timeout leaves\n // the schema still missing they RE-PROBE and THROW rather than letting\n // init memoize success against absent schema.\n await ensureTableExists(\"application_state\", createSql);\n // Older deployments created `updated_at` as 32-bit `INTEGER`; on Postgres\n // the `Date.now()` written by appStatePut() on every turn overflows int4.\n // Widen it in place (no-op once done / on fresh BIGINT databases).\n await widenIntColumnsToBigInt(\"application_state\", [\"updated_at\"]);\n // Indexes for the two hot poll paths. Probe pg_indexes first (no lock)\n // and skip the SHARE-locking CREATE INDEX when already present.\n await ensureIndexExists(\n \"app_state_updated_at_idx\",\n `CREATE INDEX IF NOT EXISTS app_state_updated_at_idx ON application_state (updated_at)`,\n );\n await ensureIndexExists(\n \"app_state_key_updated_idx\",\n `CREATE INDEX IF NOT EXISTS app_state_key_updated_idx ON application_state (key, updated_at)`,\n );\n return;\n }\n\n // SQLite (local dev): no lock problem — keep the original behaviour.\n await client.execute(createSql);\n // Older deployments created `updated_at` as 32-bit `INTEGER`; on Postgres\n // the `Date.now()` written by appStatePut() on every turn overflows int4.\n // Widen it in place (no-op once done / on fresh BIGINT databases).\n await widenIntColumnsToBigInt(\"application_state\", [\"updated_at\"]);\n // Indexes for the two hot poll paths:\n // - `SELECT … WHERE updated_at > ?` (watermark scan, every poll cycle)\n // - `SELECT … WHERE key = ? … ORDER BY updated_at ASC` (marker lookups)\n // Both are dialect-agnostic (no DESC/partial/PG-only syntax) so they\n // apply identically on SQLite and Postgres. IF NOT EXISTS makes them\n // idempotent across restarts on existing databases.\n for (const ddl of [\n `CREATE INDEX IF NOT EXISTS app_state_updated_at_idx ON application_state (updated_at)`,\n `CREATE INDEX IF NOT EXISTS app_state_key_updated_idx ON application_state (key, updated_at)`,\n ]) {\n try {\n await client.execute(ddl);\n } catch {\n // Index already exists or the dialect rejected a duplicate.\n }\n }\n })().catch((err) => {\n // Retry init on the next call after a failed startup.\n _initPromise = undefined;\n throw err;\n });\n }\n return _initPromise;\n}\n\nexport async function appStateGet(\n sessionId: string,\n key: string,\n): Promise<Record<string, unknown> | null> {\n try {\n await ensureTable();\n const client = getDbExec();\n const { rows } = await client.execute({\n sql: `SELECT value FROM application_state WHERE session_id = ? AND key = ?`,\n args: [sessionId, key],\n });\n if (rows.length === 0) return null;\n return JSON.parse(rows[0].value as string);\n } catch (err) {\n // Transient WS / connection drops (Neon serverless) — caller polls every\n // 2s and will see the value on the next tick. Swallow rather than 500.\n if (isConnectionError(err)) return null;\n throw err;\n }\n}\n\n/**\n * Read several application-state keys for one session in a single SQL query.\n * Missing keys are returned as `null` so callers can preserve the requested\n * shape without issuing one fallback query per key.\n */\nexport async function appStateGetMany(\n sessionId: string,\n keys: readonly string[],\n): Promise<Record<string, Record<string, unknown> | null>> {\n const uniqueKeys = [...new Set(keys)];\n const values: Record<string, Record<string, unknown> | null> = {};\n for (const key of uniqueKeys) values[key] = null;\n if (uniqueKeys.length === 0) return values;\n\n try {\n await ensureTable();\n const client = getDbExec();\n const placeholders = uniqueKeys.map(() => \"?\").join(\", \");\n const { rows } = await client.execute({\n sql: `SELECT key, value FROM application_state WHERE session_id = ? AND key IN (${placeholders})`,\n args: [sessionId, ...uniqueKeys],\n });\n for (const row of rows) {\n values[row.key as string] = JSON.parse(row.value as string);\n }\n return values;\n } catch (err) {\n if (isConnectionError(err)) return values;\n throw err;\n }\n}\n\nexport async function appStatePut(\n sessionId: string,\n key: string,\n value: Record<string, unknown>,\n options?: StoreWriteOptions,\n): Promise<void> {\n await ensureTable();\n const client = getDbExec();\n const serialized = JSON.stringify(value);\n if (\n !isLocalDatabase() &&\n Buffer.byteLength(serialized, \"utf8\") > MAX_HOSTED_APP_STATE_VALUE_BYTES\n ) {\n throw new Error(\n `application_state value \"${key}\" is too large for hosted SQL storage. Store large files, base64, or blobs in file storage and write only a URL or handle.`,\n );\n }\n await client.execute({\n sql: isPostgres()\n ? `INSERT INTO application_state (session_id, key, value, updated_at) VALUES (?, ?, ?, ?) ON CONFLICT (session_id, key) DO UPDATE SET value=EXCLUDED.value, updated_at=EXCLUDED.updated_at`\n : `INSERT OR REPLACE INTO application_state (session_id, key, value, updated_at) VALUES (?, ?, ?, ?)`,\n args: [sessionId, key, serialized, Date.now()],\n });\n emitAppStateChange(key, options?.requestSource, sessionId);\n}\n\nexport async function appStateDelete(\n sessionId: string,\n key: string,\n options?: StoreWriteOptions,\n): Promise<boolean> {\n await ensureTable();\n const client = getDbExec();\n const result = await client.execute({\n sql: `DELETE FROM application_state WHERE session_id = ? AND key = ?`,\n args: [sessionId, key],\n });\n const deleted = result.rowsAffected > 0;\n if (deleted) emitAppStateDelete(key, options?.requestSource, sessionId);\n return deleted;\n}\n\nexport async function appStateCompareAndSet(\n sessionId: string,\n key: string,\n expectedValue: Record<string, unknown> | null,\n nextValue: Record<string, unknown> | null,\n options?: StoreWriteOptions,\n): Promise<boolean> {\n await ensureTable();\n const client = getDbExec();\n const changed = await executeAppStateCompareAndSet(\n client,\n sessionId,\n key,\n expectedValue,\n nextValue,\n );\n if (changed) {\n if (nextValue === null) {\n emitAppStateDelete(key, options?.requestSource, sessionId);\n } else {\n emitAppStateChange(key, options?.requestSource, sessionId);\n }\n }\n return changed;\n}\n\nexport interface AppStateCompareAndSetOperation {\n key: string;\n expectedValue: Record<string, unknown> | null;\n nextValue: Record<string, unknown> | null;\n}\n\nconst APP_STATE_CAS_MISMATCH = Symbol(\"app-state-cas-mismatch\");\n\nasync function executeAppStateCompareAndSet(\n client: DbExec,\n sessionId: string,\n key: string,\n expectedValue: Record<string, unknown> | null,\n nextValue: Record<string, unknown> | null,\n): Promise<boolean> {\n const statement = buildAppStateCompareAndSetStatement(\n sessionId,\n key,\n expectedValue,\n nextValue,\n );\n const result = await client.execute(statement);\n return result.rowsAffected > 0;\n}\n\nfunction buildAppStateCompareAndSetStatement(\n sessionId: string,\n key: string,\n expectedValue: Record<string, unknown> | null,\n nextValue: Record<string, unknown> | null,\n): { sql: string; args: unknown[] } {\n if (expectedValue === null) {\n if (nextValue === null) {\n throw new Error(\n \"Application state CAS cannot replace absence with absence.\",\n );\n }\n const next = serializeAppStateValue(key, nextValue);\n return {\n sql: isPostgres()\n ? `INSERT INTO application_state (session_id, key, value, updated_at) SELECT ?, ?, ?, ? WHERE NOT EXISTS (SELECT 1 FROM application_state WHERE session_id = ? AND key = ?) ON CONFLICT (session_id, key) DO NOTHING`\n : `INSERT OR IGNORE INTO application_state (session_id, key, value, updated_at) SELECT ?, ?, ?, ? WHERE NOT EXISTS (SELECT 1 FROM application_state WHERE session_id = ? AND key = ?)`,\n args: [sessionId, key, next, Date.now(), sessionId, key],\n };\n }\n\n const expected = JSON.stringify(expectedValue);\n if (nextValue === null) {\n return {\n sql: `DELETE FROM application_state WHERE session_id = ? AND key = ? AND value = ?`,\n args: [sessionId, key, expected],\n };\n }\n\n const next = serializeAppStateValue(key, nextValue);\n return {\n sql: `UPDATE application_state SET value = ?, updated_at = ? WHERE session_id = ? AND key = ? AND value = ?`,\n args: [next, Date.now(), sessionId, key, expected],\n };\n}\n\nfunction buildD1CompareAndSetGuard(\n sessionId: string,\n guardKey: string,\n operation: AppStateCompareAndSetOperation,\n): { sql: string; args: unknown[] } {\n const expected = operation.expectedValue;\n const condition =\n expected === null\n ? \"NOT EXISTS (SELECT 1 FROM application_state WHERE session_id = ? AND key = ?)\"\n : \"EXISTS (SELECT 1 FROM application_state WHERE session_id = ? AND key = ? AND value = ?)\";\n return {\n sql: `INSERT INTO application_state (session_id, key, value, updated_at) SELECT ?, ?, '{}', ? WHERE NOT (${condition})`,\n args:\n expected === null\n ? [sessionId, guardKey, Date.now(), sessionId, operation.key]\n : [\n sessionId,\n guardKey,\n Date.now(),\n sessionId,\n operation.key,\n JSON.stringify(expected),\n ],\n };\n}\n\nfunction isD1CompareAndSetMismatch(error: unknown): boolean {\n const message = error instanceof Error ? error.message : String(error);\n return /unique constraint failed:\\s*application_state\\.session_id,\\s*application_state\\.key/i.test(\n message,\n );\n}\n\nfunction serializeAppStateValue(\n key: string,\n value: Record<string, unknown>,\n): string {\n const serialized = JSON.stringify(value);\n if (\n !isLocalDatabase() &&\n Buffer.byteLength(serialized, \"utf8\") > MAX_HOSTED_APP_STATE_VALUE_BYTES\n ) {\n throw new Error(\n `application_state value \"${key}\" is too large for hosted SQL storage. Store large files, base64, or blobs in file storage and write only a URL or handle.`,\n );\n }\n return serialized;\n}\n\nexport async function appStateCompareAndSetMany(\n sessionId: string,\n operations: readonly AppStateCompareAndSetOperation[],\n options?: StoreWriteOptions,\n): Promise<boolean> {\n if (operations.length === 0) return true;\n const keys = new Set(operations.map(({ key }) => key));\n if (keys.size !== operations.length) {\n throw new Error(\"Application state multi-key CAS requires unique keys.\");\n }\n for (const { key, nextValue } of operations) {\n if (nextValue) serializeAppStateValue(key, nextValue);\n }\n const orderedOperations = [...operations].sort((a, b) =>\n a.key.localeCompare(b.key),\n );\n\n await ensureTable();\n const client = getDbExec();\n if (getDialect() === \"d1\") {\n if (!client.atomicBatch) {\n throw new Error(\n \"D1 application-state CAS requires atomic batch support.\",\n );\n }\n const guardKey = `__agent_native_cas_guard__:${Date.now()}:${Math.random().toString(36).slice(2)}`;\n const guardInsert = {\n sql: `INSERT INTO application_state (session_id, key, value, updated_at) VALUES (?, ?, '{}', ?)`,\n args: [sessionId, guardKey, Date.now()],\n };\n const guards = orderedOperations.map((operation) =>\n buildD1CompareAndSetGuard(sessionId, guardKey, operation),\n );\n const mutations = orderedOperations.map((operation) =>\n buildAppStateCompareAndSetStatement(\n sessionId,\n operation.key,\n operation.expectedValue,\n operation.nextValue,\n ),\n );\n let results;\n try {\n results = await client.atomicBatch([\n guardInsert,\n ...guards,\n ...mutations,\n {\n sql: `DELETE FROM application_state WHERE session_id = ? AND key = ?`,\n args: [sessionId, guardKey],\n },\n ]);\n } catch (error) {\n if (isD1CompareAndSetMismatch(error)) return false;\n throw error;\n }\n const mutationResults = results.slice(\n 1 + guards.length,\n 1 + guards.length + mutations.length,\n );\n if (\n mutationResults.length !== mutations.length ||\n mutationResults.some((result) => result.rowsAffected !== 1)\n ) {\n throw new Error(\n \"D1 application-state CAS completed without applying every mutation.\",\n );\n }\n } else {\n if (!client.transaction) {\n throw new Error(\"Application state multi-key CAS requires transactions.\");\n }\n try {\n await client.transaction(async (tx) => {\n for (const operation of orderedOperations) {\n const changed = await executeAppStateCompareAndSet(\n tx,\n sessionId,\n operation.key,\n operation.expectedValue,\n operation.nextValue,\n );\n if (!changed) throw APP_STATE_CAS_MISMATCH;\n }\n });\n } catch (error) {\n if (error === APP_STATE_CAS_MISMATCH) return false;\n throw error;\n }\n }\n\n for (const { key, nextValue } of operations) {\n if (nextValue === null) {\n emitAppStateDelete(key, options?.requestSource, sessionId);\n } else {\n emitAppStateChange(key, options?.requestSource, sessionId);\n }\n }\n return true;\n}\n\nexport async function appStateList(\n sessionId: string,\n keyPrefix: string,\n): Promise<Array<{ key: string; value: Record<string, unknown> }>> {\n await ensureTable();\n const client = getDbExec();\n const { rows } = await client.execute({\n sql: `SELECT key, value FROM application_state WHERE session_id = ? AND key LIKE ? ESCAPE '!'`,\n args: [sessionId, escapeLike(keyPrefix) + \"%\"],\n });\n return rows.map((row) => ({\n key: row.key as string,\n value: JSON.parse(row.value as string),\n }));\n}\n\nexport async function appStateDeleteByPrefix(\n sessionId: string,\n keyPrefix: string,\n options?: StoreWriteOptions,\n): Promise<number> {\n await ensureTable();\n const client = getDbExec();\n\n // Get keys first so we can emit events\n const { rows } = await client.execute({\n sql: `SELECT key FROM application_state WHERE session_id = ? AND key LIKE ? ESCAPE '!'`,\n args: [sessionId, escapeLike(keyPrefix) + \"%\"],\n });\n\n if (rows.length === 0) return 0;\n\n const result = await client.execute({\n sql: `DELETE FROM application_state WHERE session_id = ? AND key LIKE ? ESCAPE '!'`,\n args: [sessionId, escapeLike(keyPrefix) + \"%\"],\n });\n\n for (const row of rows) {\n emitAppStateDelete(row.key as string, options?.requestSource, sessionId);\n }\n\n return result.rowsAffected;\n}\n"]}
|
|
1
|
+
{"version":3,"file":"store.js","sourceRoot":"","sources":["../../src/application-state/store.ts"],"names":[],"mappings":"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","sourcesContent":["import {\n getDbExec,\n getDialect,\n isLocalDatabase,\n isPostgres,\n intType,\n type DbExec,\n} from \"../db/client.js\";\nimport { ensureIndexExists, ensureTableExists } from \"../db/ddl-guard.js\";\nimport { widenIntColumnsToBigInt } from \"../db/widen-columns.js\";\nimport type { StoreWriteOptions } from \"../settings/store.js\";\nimport { emitAppStateChange, emitAppStateDelete } from \"./emitter.js\";\n\nlet _initPromise: Promise<void> | undefined;\nconst MAX_HOSTED_APP_STATE_VALUE_BYTES = 1024 * 1024;\n\n// Escapes LIKE wildcards (`%`, `_`) and the escape char itself so a caller's\n// literal prefix is matched verbatim. Used with `ESCAPE '!'` in prefix queries\n// below; without this, a prefix such as `user_settings` would treat `_` as a\n// single-char wildcard and over-match (e.g. delete `userXsettings`).\nfunction escapeLike(s: string): string {\n return s.replace(/[!%_]/g, (match) => `!${match}`);\n}\n\nasync function ensureTable(): Promise<void> {\n if (!_initPromise) {\n _initPromise = (async () => {\n const client = getDbExec();\n const createSql = `\n CREATE TABLE IF NOT EXISTS application_state (\n session_id TEXT NOT NULL,\n key TEXT NOT NULL,\n value TEXT NOT NULL,\n updated_at ${intType()} NOT NULL,\n PRIMARY KEY (session_id, key)\n )\n `;\n\n if (isPostgres()) {\n // Hot path: the `application_state` table and its poll indexes are\n // virtually always already present in production. Issuing `CREATE\n // TABLE`/ `CREATE INDEX` still takes a lock that, in a fresh\n // background-worker process behind a concurrent connection on the\n // shared Neon DB, can block ~indefinitely (ACCESS EXCLUSIVE for CREATE\n // TABLE; a write-blocking SHARE lock for CREATE INDEX). The ensure*\n // wrappers probe `information_schema`/`pg_indexes` first (plain reads,\n // no lock) and run DDL ONLY for what is actually missing, bounded by a\n // transaction-scoped `lock_timeout`. If a swallowed lock-timeout leaves\n // the schema still missing they RE-PROBE and THROW rather than letting\n // init memoize success against absent schema.\n await ensureTableExists(\"application_state\", createSql);\n // Older deployments created `updated_at` as 32-bit `INTEGER`; on Postgres\n // the `Date.now()` written by appStatePut() on every turn overflows int4.\n // Widen it in place (no-op once done / on fresh BIGINT databases).\n await widenIntColumnsToBigInt(\"application_state\", [\"updated_at\"]);\n // Indexes for the two hot poll paths. Probe pg_indexes first (no lock)\n // and skip the SHARE-locking CREATE INDEX when already present.\n await ensureIndexExists(\n \"app_state_updated_at_idx\",\n `CREATE INDEX IF NOT EXISTS app_state_updated_at_idx ON application_state (updated_at)`,\n );\n await ensureIndexExists(\n \"app_state_key_updated_idx\",\n `CREATE INDEX IF NOT EXISTS app_state_key_updated_idx ON application_state (key, updated_at)`,\n );\n return;\n }\n\n // SQLite (local dev): no lock problem — keep the original behaviour.\n await client.execute(createSql);\n // Older deployments created `updated_at` as 32-bit `INTEGER`; on Postgres\n // the `Date.now()` written by appStatePut() on every turn overflows int4.\n // Widen it in place (no-op once done / on fresh BIGINT databases).\n await widenIntColumnsToBigInt(\"application_state\", [\"updated_at\"]);\n // Indexes for the two hot poll paths:\n // - `SELECT … WHERE updated_at > ?` (watermark scan, every poll cycle)\n // - `SELECT … WHERE key = ? … ORDER BY updated_at ASC` (marker lookups)\n // Both are dialect-agnostic (no DESC/partial/PG-only syntax) so they\n // apply identically on SQLite and Postgres. IF NOT EXISTS makes them\n // idempotent across restarts on existing databases.\n for (const ddl of [\n `CREATE INDEX IF NOT EXISTS app_state_updated_at_idx ON application_state (updated_at)`,\n `CREATE INDEX IF NOT EXISTS app_state_key_updated_idx ON application_state (key, updated_at)`,\n ]) {\n try {\n await client.execute(ddl);\n } catch {\n // Index already exists or the dialect rejected a duplicate.\n }\n }\n })().catch((err) => {\n // Retry init on the next call after a failed startup.\n _initPromise = undefined;\n throw err;\n });\n }\n return _initPromise;\n}\n\nexport async function appStateGet(\n sessionId: string,\n key: string,\n): Promise<Record<string, unknown> | null> {\n await ensureTable();\n const client = getDbExec();\n const { rows } = await client.execute({\n sql: `SELECT value FROM application_state WHERE session_id = ? AND key = ?`,\n args: [sessionId, key],\n });\n if (rows.length === 0) return null;\n return JSON.parse(rows[0].value as string);\n}\n\n/**\n * Read several application-state keys for one session in a single SQL query.\n * Missing keys are returned as `null` so callers can preserve the requested\n * shape without issuing one fallback query per key.\n */\nexport async function appStateGetMany(\n sessionId: string,\n keys: readonly string[],\n): Promise<Record<string, Record<string, unknown> | null>> {\n const uniqueKeys = [...new Set(keys)];\n const values: Record<string, Record<string, unknown> | null> = {};\n for (const key of uniqueKeys) values[key] = null;\n if (uniqueKeys.length === 0) return values;\n\n await ensureTable();\n const client = getDbExec();\n const placeholders = uniqueKeys.map(() => \"?\").join(\", \");\n const { rows } = await client.execute({\n sql: `SELECT key, value FROM application_state WHERE session_id = ? AND key IN (${placeholders})`,\n args: [sessionId, ...uniqueKeys],\n });\n for (const row of rows) {\n values[row.key as string] = JSON.parse(row.value as string);\n }\n return values;\n}\n\nexport async function appStatePut(\n sessionId: string,\n key: string,\n value: Record<string, unknown>,\n options?: StoreWriteOptions,\n): Promise<void> {\n await ensureTable();\n const client = getDbExec();\n const serialized = JSON.stringify(value);\n if (\n !isLocalDatabase() &&\n Buffer.byteLength(serialized, \"utf8\") > MAX_HOSTED_APP_STATE_VALUE_BYTES\n ) {\n throw new Error(\n `application_state value \"${key}\" is too large for hosted SQL storage. Store large files, base64, or blobs in file storage and write only a URL or handle.`,\n );\n }\n await client.execute({\n sql: isPostgres()\n ? `INSERT INTO application_state (session_id, key, value, updated_at) VALUES (?, ?, ?, ?) ON CONFLICT (session_id, key) DO UPDATE SET value=EXCLUDED.value, updated_at=EXCLUDED.updated_at`\n : `INSERT OR REPLACE INTO application_state (session_id, key, value, updated_at) VALUES (?, ?, ?, ?)`,\n args: [sessionId, key, serialized, Date.now()],\n });\n emitAppStateChange(key, options?.requestSource, sessionId);\n}\n\nexport async function appStateDelete(\n sessionId: string,\n key: string,\n options?: StoreWriteOptions,\n): Promise<boolean> {\n await ensureTable();\n const client = getDbExec();\n const result = await client.execute({\n sql: `DELETE FROM application_state WHERE session_id = ? AND key = ?`,\n args: [sessionId, key],\n });\n const deleted = result.rowsAffected > 0;\n if (deleted) emitAppStateDelete(key, options?.requestSource, sessionId);\n return deleted;\n}\n\nexport async function appStateCompareAndSet(\n sessionId: string,\n key: string,\n expectedValue: Record<string, unknown> | null,\n nextValue: Record<string, unknown> | null,\n options?: StoreWriteOptions,\n): Promise<boolean> {\n await ensureTable();\n const client = getDbExec();\n const changed = await executeAppStateCompareAndSet(\n client,\n sessionId,\n key,\n expectedValue,\n nextValue,\n );\n if (changed) {\n if (nextValue === null) {\n emitAppStateDelete(key, options?.requestSource, sessionId);\n } else {\n emitAppStateChange(key, options?.requestSource, sessionId);\n }\n }\n return changed;\n}\n\nexport interface AppStateCompareAndSetOperation {\n key: string;\n expectedValue: Record<string, unknown> | null;\n nextValue: Record<string, unknown> | null;\n}\n\nconst APP_STATE_CAS_MISMATCH = Symbol(\"app-state-cas-mismatch\");\n\nasync function executeAppStateCompareAndSet(\n client: DbExec,\n sessionId: string,\n key: string,\n expectedValue: Record<string, unknown> | null,\n nextValue: Record<string, unknown> | null,\n): Promise<boolean> {\n const statement = buildAppStateCompareAndSetStatement(\n sessionId,\n key,\n expectedValue,\n nextValue,\n );\n const result = await client.execute(statement);\n return result.rowsAffected > 0;\n}\n\nfunction buildAppStateCompareAndSetStatement(\n sessionId: string,\n key: string,\n expectedValue: Record<string, unknown> | null,\n nextValue: Record<string, unknown> | null,\n): { sql: string; args: unknown[] } {\n if (expectedValue === null) {\n if (nextValue === null) {\n throw new Error(\n \"Application state CAS cannot replace absence with absence.\",\n );\n }\n const next = serializeAppStateValue(key, nextValue);\n return {\n sql: isPostgres()\n ? `INSERT INTO application_state (session_id, key, value, updated_at) SELECT ?, ?, ?, ? WHERE NOT EXISTS (SELECT 1 FROM application_state WHERE session_id = ? AND key = ?) ON CONFLICT (session_id, key) DO NOTHING`\n : `INSERT OR IGNORE INTO application_state (session_id, key, value, updated_at) SELECT ?, ?, ?, ? WHERE NOT EXISTS (SELECT 1 FROM application_state WHERE session_id = ? AND key = ?)`,\n args: [sessionId, key, next, Date.now(), sessionId, key],\n };\n }\n\n const expected = JSON.stringify(expectedValue);\n if (nextValue === null) {\n return {\n sql: `DELETE FROM application_state WHERE session_id = ? AND key = ? AND value = ?`,\n args: [sessionId, key, expected],\n };\n }\n\n const next = serializeAppStateValue(key, nextValue);\n return {\n sql: `UPDATE application_state SET value = ?, updated_at = ? WHERE session_id = ? AND key = ? AND value = ?`,\n args: [next, Date.now(), sessionId, key, expected],\n };\n}\n\nfunction buildD1CompareAndSetGuard(\n sessionId: string,\n guardKey: string,\n operation: AppStateCompareAndSetOperation,\n): { sql: string; args: unknown[] } {\n const expected = operation.expectedValue;\n const condition =\n expected === null\n ? \"NOT EXISTS (SELECT 1 FROM application_state WHERE session_id = ? AND key = ?)\"\n : \"EXISTS (SELECT 1 FROM application_state WHERE session_id = ? AND key = ? AND value = ?)\";\n return {\n sql: `INSERT INTO application_state (session_id, key, value, updated_at) SELECT ?, ?, '{}', ? WHERE NOT (${condition})`,\n args:\n expected === null\n ? [sessionId, guardKey, Date.now(), sessionId, operation.key]\n : [\n sessionId,\n guardKey,\n Date.now(),\n sessionId,\n operation.key,\n JSON.stringify(expected),\n ],\n };\n}\n\nfunction isD1CompareAndSetMismatch(error: unknown): boolean {\n const message = error instanceof Error ? error.message : String(error);\n return /unique constraint failed:\\s*application_state\\.session_id,\\s*application_state\\.key/i.test(\n message,\n );\n}\n\nfunction serializeAppStateValue(\n key: string,\n value: Record<string, unknown>,\n): string {\n const serialized = JSON.stringify(value);\n if (\n !isLocalDatabase() &&\n Buffer.byteLength(serialized, \"utf8\") > MAX_HOSTED_APP_STATE_VALUE_BYTES\n ) {\n throw new Error(\n `application_state value \"${key}\" is too large for hosted SQL storage. Store large files, base64, or blobs in file storage and write only a URL or handle.`,\n );\n }\n return serialized;\n}\n\nexport async function appStateCompareAndSetMany(\n sessionId: string,\n operations: readonly AppStateCompareAndSetOperation[],\n options?: StoreWriteOptions,\n): Promise<boolean> {\n if (operations.length === 0) return true;\n const keys = new Set(operations.map(({ key }) => key));\n if (keys.size !== operations.length) {\n throw new Error(\"Application state multi-key CAS requires unique keys.\");\n }\n for (const { key, nextValue } of operations) {\n if (nextValue) serializeAppStateValue(key, nextValue);\n }\n const orderedOperations = [...operations].sort((a, b) =>\n a.key.localeCompare(b.key),\n );\n\n await ensureTable();\n const client = getDbExec();\n if (getDialect() === \"d1\") {\n if (!client.atomicBatch) {\n throw new Error(\n \"D1 application-state CAS requires atomic batch support.\",\n );\n }\n const guardKey = `__agent_native_cas_guard__:${Date.now()}:${Math.random().toString(36).slice(2)}`;\n const guardInsert = {\n sql: `INSERT INTO application_state (session_id, key, value, updated_at) VALUES (?, ?, '{}', ?)`,\n args: [sessionId, guardKey, Date.now()],\n };\n const guards = orderedOperations.map((operation) =>\n buildD1CompareAndSetGuard(sessionId, guardKey, operation),\n );\n const mutations = orderedOperations.map((operation) =>\n buildAppStateCompareAndSetStatement(\n sessionId,\n operation.key,\n operation.expectedValue,\n operation.nextValue,\n ),\n );\n let results;\n try {\n results = await client.atomicBatch([\n guardInsert,\n ...guards,\n ...mutations,\n {\n sql: `DELETE FROM application_state WHERE session_id = ? AND key = ?`,\n args: [sessionId, guardKey],\n },\n ]);\n } catch (error) {\n if (isD1CompareAndSetMismatch(error)) return false;\n throw error;\n }\n const mutationResults = results.slice(\n 1 + guards.length,\n 1 + guards.length + mutations.length,\n );\n if (\n mutationResults.length !== mutations.length ||\n mutationResults.some((result) => result.rowsAffected !== 1)\n ) {\n throw new Error(\n \"D1 application-state CAS completed without applying every mutation.\",\n );\n }\n } else {\n if (!client.transaction) {\n throw new Error(\"Application state multi-key CAS requires transactions.\");\n }\n try {\n await client.transaction(async (tx) => {\n for (const operation of orderedOperations) {\n const changed = await executeAppStateCompareAndSet(\n tx,\n sessionId,\n operation.key,\n operation.expectedValue,\n operation.nextValue,\n );\n if (!changed) throw APP_STATE_CAS_MISMATCH;\n }\n });\n } catch (error) {\n if (error === APP_STATE_CAS_MISMATCH) return false;\n throw error;\n }\n }\n\n for (const { key, nextValue } of operations) {\n if (nextValue === null) {\n emitAppStateDelete(key, options?.requestSource, sessionId);\n } else {\n emitAppStateChange(key, options?.requestSource, sessionId);\n }\n }\n return true;\n}\n\nexport async function appStateList(\n sessionId: string,\n keyPrefix: string,\n): Promise<Array<{ key: string; value: Record<string, unknown> }>> {\n await ensureTable();\n const client = getDbExec();\n const { rows } = await client.execute({\n sql: `SELECT key, value FROM application_state WHERE session_id = ? AND key LIKE ? ESCAPE '!'`,\n args: [sessionId, escapeLike(keyPrefix) + \"%\"],\n });\n return rows.map((row) => ({\n key: row.key as string,\n value: JSON.parse(row.value as string),\n }));\n}\n\nexport async function appStateDeleteByPrefix(\n sessionId: string,\n keyPrefix: string,\n options?: StoreWriteOptions,\n): Promise<number> {\n await ensureTable();\n const client = getDbExec();\n\n // Get keys first so we can emit events\n const { rows } = await client.execute({\n sql: `SELECT key FROM application_state WHERE session_id = ? AND key LIKE ? ESCAPE '!'`,\n args: [sessionId, escapeLike(keyPrefix) + \"%\"],\n });\n\n if (rows.length === 0) return 0;\n\n const result = await client.execute({\n sql: `DELETE FROM application_state WHERE session_id = ? AND key LIKE ? ESCAPE '!'`,\n args: [sessionId, escapeLike(keyPrefix) + \"%\"],\n });\n\n for (const row of rows) {\n emitAppStateDelete(row.key as string, options?.requestSource, sessionId);\n }\n\n return result.rowsAffected;\n}\n"]}
|
package/dist/cli/create.d.ts
CHANGED
|
@@ -23,6 +23,11 @@ export interface CreateAppOptions {
|
|
|
23
23
|
* unconditional workspace scaffold.
|
|
24
24
|
*/
|
|
25
25
|
forceWorkspace?: boolean;
|
|
26
|
+
/**
|
|
27
|
+
* Internal: scaffold into the current directory instead of a new subfolder.
|
|
28
|
+
* Set when the name argument is `.`/`./` (see `createApp`).
|
|
29
|
+
*/
|
|
30
|
+
inPlace?: boolean;
|
|
26
31
|
}
|
|
27
32
|
/**
|
|
28
33
|
* Main entry for `agent-native create [name]`.
|
|
@@ -211,6 +216,8 @@ declare function rewriteAgentChatAppId(appDir: string, appName: string, template
|
|
|
211
216
|
declare function rewriteTrackingAppId(appDir: string, appName: string, templateName?: string, sourceIdentity?: ScaffoldSourceIdentity): void;
|
|
212
217
|
declare function renameGitignore(dir: string): void;
|
|
213
218
|
declare function replacePlaceholders(dir: string, appName: string, appTitle: string, workspaceName?: string): void;
|
|
214
|
-
declare function copyDir(src: string, dest: string, root?: string
|
|
219
|
+
declare function copyDir(src: string, dest: string, root?: string, opts?: {
|
|
220
|
+
skipExisting?: boolean;
|
|
221
|
+
}): void;
|
|
215
222
|
declare function shouldSkipScaffoldEntry(name: string, srcPath?: string): boolean;
|
|
216
223
|
//# sourceMappingURL=create.d.ts.map
|
package/dist/cli/create.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"create.d.ts","sourceRoot":"","sources":["../../src/cli/create.ts"],"names":[],"mappings":"AAiBA,OAAO,EAAkB,mBAAmB,EAAE,MAAM,kBAAkB,CAAC;AAKvE,QAAA,MAAM,IAAI,2BAA2B,CAAC;AACtC,QAAA,MAAM,aAAa,cAAc,CAAC;
|
|
1
|
+
{"version":3,"file":"create.d.ts","sourceRoot":"","sources":["../../src/cli/create.ts"],"names":[],"mappings":"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"}
|
package/dist/cli/create.js
CHANGED
|
@@ -39,6 +39,18 @@ const FIRST_PARTY_TARBALL_SYMLINK_EXCLUDES = [
|
|
|
39
39
|
"*/.claude/skills",
|
|
40
40
|
];
|
|
41
41
|
const localPackageTarballs = new Map();
|
|
42
|
+
/** VCS/editor files that don't count as "not empty" for an in-place scaffold. */
|
|
43
|
+
const IN_PLACE_ALLOWLIST = new Set([
|
|
44
|
+
".git",
|
|
45
|
+
".gitignore",
|
|
46
|
+
".gitattributes",
|
|
47
|
+
".DS_Store",
|
|
48
|
+
".idea",
|
|
49
|
+
".vscode",
|
|
50
|
+
"LICENSE",
|
|
51
|
+
"README.md",
|
|
52
|
+
"Thumbs.db",
|
|
53
|
+
]);
|
|
42
54
|
/**
|
|
43
55
|
* Tagged error for input that fails CLI-level validation (repo names, app
|
|
44
56
|
* names, etc.). The Sentry `beforeSend` hook in cli/index.ts drops events
|
|
@@ -89,6 +101,12 @@ const COMMUNITY_OPTION = {
|
|
|
89
101
|
*/
|
|
90
102
|
export async function createApp(name, opts) {
|
|
91
103
|
const clack = await import("@clack/prompts");
|
|
104
|
+
// `create .` (or `./`) means "scaffold into the current folder" — derive the
|
|
105
|
+
// project name from the folder's basename, like create-react-app / npm init.
|
|
106
|
+
if (name === "." || name === "./") {
|
|
107
|
+
name = path.basename(process.cwd());
|
|
108
|
+
opts = { ...opts, inPlace: true };
|
|
109
|
+
}
|
|
92
110
|
// Reject an invalid provided name before any interactive prompt so bad input
|
|
93
111
|
// fails fast instead of blocking on the start-shape picker below.
|
|
94
112
|
if (name !== undefined) {
|
|
@@ -260,6 +278,38 @@ function assertValidProjectName(name, clack) {
|
|
|
260
278
|
process.exit(1);
|
|
261
279
|
}
|
|
262
280
|
}
|
|
281
|
+
/**
|
|
282
|
+
* Resolve where a scaffold writes and guard the target. A named project writes
|
|
283
|
+
* to a new sibling subfolder that must not already exist; `create .` writes
|
|
284
|
+
* into the current directory, which must be empty apart from benign VCS/editor
|
|
285
|
+
* files (a pre-existing repo is allowed).
|
|
286
|
+
*
|
|
287
|
+
* For an in-place scaffold we do NOT return the current directory: we build
|
|
288
|
+
* into a private staging directory and `finalizeScaffold` copies the result
|
|
289
|
+
* in afterward. Staging keeps the whole scaffold atomic — a mid-scaffold
|
|
290
|
+
* failure's cleanup can only ever delete the staging dir, never the user's
|
|
291
|
+
* current directory (including its `.git`).
|
|
292
|
+
*/
|
|
293
|
+
function resolveScaffoldTarget(name, inPlace, clack) {
|
|
294
|
+
if (inPlace) {
|
|
295
|
+
const conflicting = fs
|
|
296
|
+
.readdirSync(process.cwd())
|
|
297
|
+
.filter((entry) => !IN_PLACE_ALLOWLIST.has(entry));
|
|
298
|
+
if (conflicting.length > 0) {
|
|
299
|
+
const shown = conflicting.slice(0, 3).join(", ");
|
|
300
|
+
const more = conflicting.length > 3 ? ", …" : "";
|
|
301
|
+
clack.cancel(`Current directory is not empty (${shown}${more}). Scaffold into an empty folder, or run \`create <name>\` to make a new one.`);
|
|
302
|
+
process.exit(1);
|
|
303
|
+
}
|
|
304
|
+
return fs.mkdtempSync(path.join(os.tmpdir(), "agent-native-create-"));
|
|
305
|
+
}
|
|
306
|
+
const targetDir = path.resolve(process.cwd(), name);
|
|
307
|
+
if (fs.existsSync(targetDir)) {
|
|
308
|
+
clack.cancel(`Directory "${name}" already exists.`);
|
|
309
|
+
process.exit(1);
|
|
310
|
+
}
|
|
311
|
+
return targetDir;
|
|
312
|
+
}
|
|
263
313
|
/* ─────────────────────────────────────────────────────────────────────────
|
|
264
314
|
* Workspace creation (new default)
|
|
265
315
|
* ───────────────────────────────────────────────────────────────────────── */
|
|
@@ -289,11 +339,7 @@ async function createWorkspaceInteractive(name, opts, clack) {
|
|
|
289
339
|
excludeNames: ["dispatch"],
|
|
290
340
|
});
|
|
291
341
|
const templates = ["dispatch", ...optionalPicks];
|
|
292
|
-
const targetDir =
|
|
293
|
-
if (fs.existsSync(targetDir)) {
|
|
294
|
-
clack.cancel(`Directory "${name}" already exists.`);
|
|
295
|
-
process.exit(1);
|
|
296
|
-
}
|
|
342
|
+
const targetDir = resolveScaffoldTarget(name, opts?.inPlace, clack);
|
|
297
343
|
const s = clack.spinner();
|
|
298
344
|
for (const template of templates) {
|
|
299
345
|
showCommunityTemplateTrustNote(template, clack);
|
|
@@ -362,12 +408,14 @@ async function createWorkspaceInteractive(name, opts, clack) {
|
|
|
362
408
|
catch (err) {
|
|
363
409
|
s.stop("Failed to scaffold workspace.");
|
|
364
410
|
// Remove the partially-scaffolded workspace so a retry of `agent-native
|
|
365
|
-
// create <name>` doesn't trip the "Directory already exists" guard.
|
|
411
|
+
// create <name>` doesn't trip the "Directory already exists" guard. For an
|
|
412
|
+
// in-place scaffold `targetDir` is the private staging dir, so this never
|
|
413
|
+
// touches the user's current directory.
|
|
366
414
|
cleanupOnFailure(targetDir);
|
|
367
415
|
clack.cancel(err?.message ?? String(err));
|
|
368
416
|
process.exit(1);
|
|
369
417
|
}
|
|
370
|
-
|
|
418
|
+
finalizeScaffold(targetDir, opts?.inPlace);
|
|
371
419
|
// Show the user the tree we just built so the workspace/app distinction is
|
|
372
420
|
// visible, not just described. First-time users routinely expect their
|
|
373
421
|
// workspace name to be the app — seeing apps/<template>/ subdirectories
|
|
@@ -630,11 +678,7 @@ async function scaffoldOneAppIntoWorkspace(workspace, appName, templateName, cla
|
|
|
630
678
|
async function createStandaloneApp(name, opts, clack) {
|
|
631
679
|
clack.intro("Create a new standalone agent-native app");
|
|
632
680
|
name = await promptNameIfMissing(name, clack, "app", "my-app");
|
|
633
|
-
const targetDir =
|
|
634
|
-
if (fs.existsSync(targetDir)) {
|
|
635
|
-
clack.cancel(`Directory "${name}" already exists.`);
|
|
636
|
-
process.exit(1);
|
|
637
|
-
}
|
|
681
|
+
const targetDir = resolveScaffoldTarget(name, opts?.inPlace, clack);
|
|
638
682
|
// Standalone is single-select — pick one template.
|
|
639
683
|
let template = opts?.template && !opts.template.includes(",") ? opts.template : undefined;
|
|
640
684
|
if (!template) {
|
|
@@ -666,11 +710,13 @@ async function createStandaloneApp(name, opts, clack) {
|
|
|
666
710
|
}
|
|
667
711
|
catch (err) {
|
|
668
712
|
s.stop("Failed to create app.");
|
|
713
|
+
// `targetDir` is the private staging dir for an in-place scaffold, so this
|
|
714
|
+
// only ever removes the staging copy, never the user's current directory.
|
|
669
715
|
cleanupOnFailure(targetDir);
|
|
670
716
|
clack.cancel(err?.message ?? String(err));
|
|
671
717
|
process.exit(1);
|
|
672
718
|
}
|
|
673
|
-
|
|
719
|
+
finalizeScaffold(targetDir, opts?.inPlace);
|
|
674
720
|
if (template === "headless") {
|
|
675
721
|
clack.outro([
|
|
676
722
|
"Done! Next steps:",
|
|
@@ -724,6 +770,35 @@ function cleanupOnFailure(targetDir) {
|
|
|
724
770
|
// Ignore — original error is more useful than a cleanup failure.
|
|
725
771
|
}
|
|
726
772
|
}
|
|
773
|
+
/**
|
|
774
|
+
* Land a finished scaffold in its final home and initialize git. A named
|
|
775
|
+
* scaffold is already in place, so this only inits git. An in-place scaffold
|
|
776
|
+
* (`create .`) was built in `scaffoldDir` (a staging dir); copy only the files
|
|
777
|
+
* that don't already exist into the current directory so pre-existing files
|
|
778
|
+
* (`.git`, `README.md`, `.gitignore`, editor configs) are preserved, then drop
|
|
779
|
+
* the staging dir. Git init/commit is skipped when the current directory is
|
|
780
|
+
* already a repo so we never write an unexpected commit into the user's
|
|
781
|
+
* history.
|
|
782
|
+
*/
|
|
783
|
+
function finalizeScaffold(scaffoldDir, inPlace) {
|
|
784
|
+
if (!inPlace) {
|
|
785
|
+
tryGitInitUnlessRepo(scaffoldDir);
|
|
786
|
+
return;
|
|
787
|
+
}
|
|
788
|
+
const dest = process.cwd();
|
|
789
|
+
try {
|
|
790
|
+
copyDir(scaffoldDir, dest, undefined, { skipExisting: true });
|
|
791
|
+
}
|
|
792
|
+
finally {
|
|
793
|
+
cleanupOnFailure(scaffoldDir);
|
|
794
|
+
}
|
|
795
|
+
tryGitInitUnlessRepo(dest);
|
|
796
|
+
}
|
|
797
|
+
function tryGitInitUnlessRepo(dir) {
|
|
798
|
+
if (fs.existsSync(path.join(dir, ".git")))
|
|
799
|
+
return;
|
|
800
|
+
tryGitInit(dir);
|
|
801
|
+
}
|
|
727
802
|
/**
|
|
728
803
|
* Scaffold a single app template into `targetDir`. Resolves:
|
|
729
804
|
* - "headless" / legacy "blank" → bundled action-first template
|
|
@@ -2538,14 +2613,21 @@ function replacePlaceholders(dir, appName, appTitle, workspaceName) {
|
|
|
2538
2613
|
fs.writeFileSync(p, next);
|
|
2539
2614
|
}
|
|
2540
2615
|
}
|
|
2541
|
-
function copyDir(src, dest, root) {
|
|
2616
|
+
function copyDir(src, dest, root, opts) {
|
|
2542
2617
|
const resolvedRoot = root ?? path.resolve(src);
|
|
2618
|
+
const skipExisting = opts?.skipExisting ?? false;
|
|
2543
2619
|
fs.mkdirSync(dest, { recursive: true });
|
|
2544
2620
|
for (const entry of fs.readdirSync(src, { withFileTypes: true })) {
|
|
2545
2621
|
const srcPath = path.join(src, entry.name);
|
|
2546
2622
|
if (shouldSkipScaffoldEntry(entry.name, srcPath))
|
|
2547
2623
|
continue;
|
|
2548
2624
|
const destPath = path.join(dest, entry.name);
|
|
2625
|
+
// Preserve anything already at the destination (in-place scaffold merges
|
|
2626
|
+
// into a directory the user may already own). Directories still recurse so
|
|
2627
|
+
// new files land inside a pre-existing folder.
|
|
2628
|
+
if (skipExisting && !entry.isDirectory() && fs.existsSync(destPath)) {
|
|
2629
|
+
continue;
|
|
2630
|
+
}
|
|
2549
2631
|
if (entry.isSymbolicLink()) {
|
|
2550
2632
|
const target = fs.readlinkSync(srcPath);
|
|
2551
2633
|
const resolvedTarget = path.resolve(path.dirname(srcPath), target);
|
|
@@ -2556,7 +2638,7 @@ function copyDir(src, dest, root) {
|
|
|
2556
2638
|
try {
|
|
2557
2639
|
const stat = fs.statSync(srcPath);
|
|
2558
2640
|
if (stat.isDirectory()) {
|
|
2559
|
-
copyDir(srcPath, destPath, resolvedRoot);
|
|
2641
|
+
copyDir(srcPath, destPath, resolvedRoot, opts);
|
|
2560
2642
|
}
|
|
2561
2643
|
else {
|
|
2562
2644
|
fs.copyFileSync(srcPath, destPath);
|
|
@@ -2568,7 +2650,7 @@ function copyDir(src, dest, root) {
|
|
|
2568
2650
|
}
|
|
2569
2651
|
}
|
|
2570
2652
|
else if (entry.isDirectory()) {
|
|
2571
|
-
copyDir(srcPath, destPath, resolvedRoot);
|
|
2653
|
+
copyDir(srcPath, destPath, resolvedRoot, opts);
|
|
2572
2654
|
}
|
|
2573
2655
|
else {
|
|
2574
2656
|
fs.copyFileSync(srcPath, destPath);
|