@agent-native/core 0.203.2-nightly-20261007213109 → 0.204.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.
Files changed (121) hide show
  1. package/dist/a2a/handlers.js +57 -1
  2. package/dist/a2a/server.js +37 -1
  3. package/dist/action.d.ts +1 -0
  4. package/dist/action.js +26 -3
  5. package/dist/agent/contain-principal.d.ts +9 -0
  6. package/dist/agent/contain-principal.js +53 -0
  7. package/dist/agent/production-agent.js +32 -2
  8. package/dist/agent/run-manager.js +14 -2
  9. package/dist/agent/run-store.js +66 -12
  10. package/dist/audit/actions/export-audit-events.d.ts +1 -1
  11. package/dist/audit/actions/export-audit-events.js +2 -2
  12. package/dist/audit/actions/export-audit-ocsf.d.ts +11 -0
  13. package/dist/audit/actions/export-audit-ocsf.js +183 -0
  14. package/dist/audit/actions/list-audit-events.d.ts +1 -1
  15. package/dist/audit/actions/list-audit-events.js +3 -3
  16. package/dist/audit/config.d.ts +1 -1
  17. package/dist/audit/config.js +5 -1
  18. package/dist/audit/index.d.ts +1 -0
  19. package/dist/audit/index.js +1 -0
  20. package/dist/audit/ocsf.d.ts +70 -0
  21. package/dist/audit/ocsf.js +228 -0
  22. package/dist/audit/record.js +1 -1
  23. package/dist/audit/store.js +9 -2
  24. package/dist/audit/types.d.ts +12 -1
  25. package/dist/cli/agent.js +2 -11
  26. package/dist/cli/connect.js +7 -2
  27. package/dist/client/use-db-sync.d.ts +13 -0
  28. package/dist/client/use-db-sync.js +22 -1
  29. package/dist/coding-tools/run-code.js +22 -1
  30. package/dist/coding-tools/sandbox/local-child-process-adapter.js +20 -0
  31. package/dist/collab/client.js +36 -3
  32. package/dist/collab/routes.d.ts +9 -4
  33. package/dist/collab/routes.js +15 -1
  34. package/dist/collab/struct-routes.d.ts +1 -1
  35. package/dist/deploy/build.js +15 -8
  36. package/dist/deploy/workspace-deploy.js +88 -13
  37. package/dist/file-upload/actions/upload-image.d.ts +1 -1
  38. package/dist/file-upload/builder.js +18 -4
  39. package/dist/framework-tools.js +3 -0
  40. package/dist/identity/rekey.js +6 -0
  41. package/dist/jobs/actions/manage-recurring-job.d.ts +1 -1
  42. package/dist/localization/core-messages/supplemental/ar-SA.js +15 -0
  43. package/dist/localization/core-messages/supplemental/de-DE.js +15 -0
  44. package/dist/localization/core-messages/supplemental/en-US.js +15 -0
  45. package/dist/localization/core-messages/supplemental/es-ES.js +15 -0
  46. package/dist/localization/core-messages/supplemental/fr-FR.js +15 -0
  47. package/dist/localization/core-messages/supplemental/hi-IN.js +15 -0
  48. package/dist/localization/core-messages/supplemental/ja-JP.js +15 -0
  49. package/dist/localization/core-messages/supplemental/ko-KR.js +15 -0
  50. package/dist/localization/core-messages/supplemental/pt-BR.js +15 -0
  51. package/dist/localization/core-messages/supplemental/zh-CN.js +15 -0
  52. package/dist/localization/core-messages/supplemental/zh-TW.js +15 -0
  53. package/dist/localization/mcp-settings-messages.d.ts +15 -0
  54. package/dist/mcp/actions/create-org-service-token.d.ts +8 -0
  55. package/dist/mcp/actions/create-org-service-token.js +93 -4
  56. package/dist/mcp/actions/list-org-service-tokens.d.ts +2 -0
  57. package/dist/mcp/actions/list-org-service-tokens.js +36 -8
  58. package/dist/mcp/actions/service-principal-input.d.ts +48 -0
  59. package/dist/mcp/actions/service-principal-input.js +102 -0
  60. package/dist/mcp/actions/set-service-principal-lifecycle.d.ts +15 -0
  61. package/dist/mcp/actions/set-service-principal-lifecycle.js +150 -0
  62. package/dist/mcp/actions/set-service-principal-policy.d.ts +14 -0
  63. package/dist/mcp/actions/set-service-principal-policy.js +105 -0
  64. package/dist/mcp/build-server.js +117 -13
  65. package/dist/mcp/connect-route.js +116 -4
  66. package/dist/mcp/connect-store.d.ts +7 -0
  67. package/dist/mcp/connect-store.js +21 -14
  68. package/dist/mcp-client/oauth-routes.js +2 -1
  69. package/dist/notifications/routes.d.ts +3 -3
  70. package/dist/observability/routes.d.ts +7 -7
  71. package/dist/org/index.d.ts +2 -2
  72. package/dist/org/service-principal-guard.d.ts +64 -0
  73. package/dist/org/service-principal-guard.js +140 -0
  74. package/dist/org/service-principal-policy.d.ts +104 -0
  75. package/dist/org/service-principal-policy.js +272 -0
  76. package/dist/provider-api/actions/custom-provider-registration.d.ts +5 -5
  77. package/dist/provider-api/actions/provider-api.d.ts +13 -13
  78. package/dist/scripts/dev/index.js +2 -8
  79. package/dist/scripts/parse-args.d.ts +7 -0
  80. package/dist/scripts/parse-args.js +76 -0
  81. package/dist/scripts/utils.d.ts +1 -1
  82. package/dist/scripts/utils.js +1 -1
  83. package/dist/secrets/routes.d.ts +11 -11
  84. package/dist/server/action-discovery.js +16 -42
  85. package/dist/server/agent-chat/script-entries.js +3 -8
  86. package/dist/server/agent-chat-plugin.js +8 -42
  87. package/dist/server/agent-engine-default-model-route.d.ts +2 -2
  88. package/dist/server/agent-engine-ollama-models-route.d.ts +1 -1
  89. package/dist/server/auth.js +5 -2
  90. package/dist/server/core-routes-plugin.js +25 -6
  91. package/dist/server/google-oauth.js +8 -4
  92. package/dist/server/identity-sso.js +2 -1
  93. package/dist/server/index.d.ts +1 -0
  94. package/dist/server/index.js +1 -0
  95. package/dist/server/poll.d.ts +9 -0
  96. package/dist/server/poll.js +86 -7
  97. package/dist/server/query-echo-safe-redirect.d.ts +10 -0
  98. package/dist/server/query-echo-safe-redirect.js +102 -0
  99. package/dist/server/release-schema.js +4 -0
  100. package/dist/server/workspace-provider-oauth.js +2 -1
  101. package/dist/templates/chat/.agents/skills/actions/references/action-fields.md +1 -0
  102. package/dist/templates/default/.agents/skills/actions/references/action-fields.md +1 -0
  103. package/dist/templates/factory/.agents/skills/review-latest-feedback/SKILL.md +24 -25
  104. package/dist/templates/factory/.agents/skills/review-latest-feedback/references/slack-reaction-replies.md +33 -0
  105. package/dist/templates/headless/.agents/skills/actions/references/action-fields.md +1 -0
  106. package/dist/templates/workspace-core/.agents/skills/actions/references/action-fields.md +1 -0
  107. package/dist/triggers/actions/manage-automation.d.ts +7 -7
  108. package/dist/vite/client.js +1 -0
  109. package/docs/content/audit-log.mdx +15 -0
  110. package/docs/content/external-agents-catalog.mdx +19 -0
  111. package/docs/content/http-api.mdx +21 -2
  112. package/docs/content/pr-visual-recap.mdx +3 -1
  113. package/docs/content/template-clips-embed.mdx +1 -1
  114. package/docs/content/turn-into-app.mdx +118 -0
  115. package/package.json +4 -4
  116. package/src/templates/chat/.agents/skills/actions/references/action-fields.md +1 -0
  117. package/src/templates/default/.agents/skills/actions/references/action-fields.md +1 -0
  118. package/src/templates/factory/.agents/skills/review-latest-feedback/SKILL.md +24 -25
  119. package/src/templates/factory/.agents/skills/review-latest-feedback/references/slack-reaction-replies.md +33 -0
  120. package/src/templates/headless/.agents/skills/actions/references/action-fields.md +1 -0
  121. package/src/templates/workspace-core/.agents/skills/actions/references/action-fields.md +1 -0
@@ -0,0 +1,272 @@
1
+ /**
2
+ * Governance record for an org service principal (`svc-<name>@service.<orgId>`).
3
+ *
4
+ * A service token proves WHO is calling; this record says who is ACCOUNTABLE
5
+ * for the principal, whether it may run at all, and which actions it may call.
6
+ * Every entry point that admits a service identity (MCP admission, MCP tool
7
+ * dispatch, agent run start) reads it through `evaluateServicePrincipal` so
8
+ * suspend and the action grant are enforced in one place.
9
+ *
10
+ * Constraints that must survive future edits:
11
+ *
12
+ * 1. No row means a legacy principal minted before governance existed: it
13
+ * stays active and unrestricted ("ungoverned"). Tightening it is an explicit
14
+ * admin act, never a side effect of upgrading.
15
+ * 2. An unreadable row is NOT an absent row. A read failure resolves to
16
+ * "unavailable" and callers refuse the request (503), because answering
17
+ * "ungoverned" would let a suspended principal back in during an outage.
18
+ * 3. A grant is deny-by-default once `allowedActions` is a list: an empty list
19
+ * grants nothing, and `null` is the only unrestricted value.
20
+ */
21
+ import { getDbExec, withDbExec } from "../db/client.js";
22
+ import { ensureTableExists } from "../db/ddl-guard.js";
23
+ import { parseServiceIdentityEmail } from "./service-identity.js";
24
+ export const SERVICE_PRINCIPAL_LIFECYCLES = [
25
+ "active",
26
+ "suspended",
27
+ "retired",
28
+ ];
29
+ export const SERVICE_PRINCIPAL_RISK_TIERS = ["low", "medium", "high"];
30
+ export const MAX_SERVICE_PRINCIPAL_ACTIONS = 200;
31
+ const SERVICE_PRINCIPAL_ACTION_PATTERN = /^[A-Za-z0-9_.:-]+\*?$/;
32
+ export function isServicePrincipalActionPattern(value) {
33
+ return value.length <= 128 && SERVICE_PRINCIPAL_ACTION_PATTERN.test(value);
34
+ }
35
+ let _initPromise;
36
+ export async function ensureTable() {
37
+ if (!_initPromise) {
38
+ _initPromise = ensureTableExists("service_principal_policies", `CREATE TABLE IF NOT EXISTS service_principal_policies (
39
+ org_id TEXT NOT NULL,
40
+ service_name TEXT NOT NULL,
41
+ owner_email TEXT,
42
+ team TEXT,
43
+ risk_tier TEXT NOT NULL DEFAULT 'medium',
44
+ purpose TEXT,
45
+ lifecycle TEXT NOT NULL DEFAULT 'active',
46
+ allowed_actions TEXT,
47
+ lifecycle_reason TEXT,
48
+ lifecycle_changed_by TEXT,
49
+ lifecycle_changed_at BIGINT,
50
+ created_at BIGINT,
51
+ updated_at BIGINT,
52
+ PRIMARY KEY (org_id, service_name)
53
+ )`)
54
+ .then(() => undefined)
55
+ .catch((error) => {
56
+ _initPromise = undefined;
57
+ throw error;
58
+ });
59
+ }
60
+ return _initPromise;
61
+ }
62
+ function numOrNull(value) {
63
+ if (value == null)
64
+ return null;
65
+ const n = Number(value);
66
+ return Number.isFinite(n) ? n : null;
67
+ }
68
+ function parseAllowedActions(raw) {
69
+ if (raw == null)
70
+ return null;
71
+ let parsed;
72
+ try {
73
+ parsed = typeof raw === "string" ? JSON.parse(raw) : raw;
74
+ // coercion-ok: an unparseable grant fails closed to an empty (deny-all) list, never to unrestricted.
75
+ }
76
+ catch {
77
+ // A stored grant we cannot parse must not widen to "unrestricted".
78
+ return [];
79
+ }
80
+ if (!Array.isArray(parsed) ||
81
+ parsed.length > MAX_SERVICE_PRINCIPAL_ACTIONS ||
82
+ !parsed.every((item) => typeof item === "string" && isServicePrincipalActionPattern(item))) {
83
+ return [];
84
+ }
85
+ return parsed;
86
+ }
87
+ function mapRow(r) {
88
+ const lifecycle = String(r.lifecycle ?? "active");
89
+ const riskTier = String(r.risk_tier ?? r.riskTier ?? "medium");
90
+ return {
91
+ orgId: String(r.org_id ?? r.orgId),
92
+ serviceName: String(r.service_name ?? r.serviceName),
93
+ ownerEmail: (r.owner_email ?? r.ownerEmail ?? null),
94
+ team: (r.team ?? null),
95
+ riskTier: SERVICE_PRINCIPAL_RISK_TIERS.includes(riskTier)
96
+ ? riskTier
97
+ : "high",
98
+ purpose: (r.purpose ?? null),
99
+ // An unrecognized lifecycle fails closed rather than reading as active.
100
+ lifecycle: SERVICE_PRINCIPAL_LIFECYCLES.includes(lifecycle)
101
+ ? lifecycle
102
+ : "suspended",
103
+ allowedActions: parseAllowedActions(r.allowed_actions ?? r.allowedActions ?? null),
104
+ lifecycleReason: (r.lifecycle_reason ?? r.lifecycleReason ?? null),
105
+ lifecycleChangedBy: (r.lifecycle_changed_by ??
106
+ r.lifecycleChangedBy ??
107
+ null),
108
+ lifecycleChangedAt: numOrNull(r.lifecycle_changed_at ?? r.lifecycleChangedAt),
109
+ createdAt: numOrNull(r.created_at ?? r.createdAt),
110
+ updatedAt: numOrNull(r.updated_at ?? r.updatedAt),
111
+ };
112
+ }
113
+ const COLUMNS = `org_id, service_name, owner_email, team, risk_tier, purpose, lifecycle, allowed_actions, lifecycle_reason, lifecycle_changed_by, lifecycle_changed_at, created_at, updated_at`;
114
+ export function servicePrincipalLifecycleLockKey(orgId, serviceName) {
115
+ return `agent-native:service-principal-lifecycle:${orgId}:${serviceName}`;
116
+ }
117
+ export async function lockServicePrincipalLifecycle(db, orgId, serviceName) {
118
+ await db.execute({
119
+ sql: "SELECT pg_advisory_xact_lock(hashtextextended(?, 0::bigint))",
120
+ args: [servicePrincipalLifecycleLockKey(orgId, serviceName)],
121
+ });
122
+ }
123
+ export async function withServicePrincipalLifecycleLock(orgId, serviceName, work) {
124
+ await ensureTable();
125
+ const db = getDbExec();
126
+ if (!db.transaction) {
127
+ throw new Error("Service principal lifecycle requires transaction support");
128
+ }
129
+ return db.transaction(async (tx) => {
130
+ return withDbExec(tx, async () => {
131
+ await lockServicePrincipalLifecycle(tx, orgId, serviceName);
132
+ return work(tx);
133
+ });
134
+ });
135
+ }
136
+ /** Throws when the store cannot be read; `null` means there is no record. */
137
+ export async function getServicePrincipalPolicy(orgId, serviceName, db = getDbExec()) {
138
+ await ensureTable();
139
+ const { rows } = await db.execute({
140
+ sql: `SELECT ${COLUMNS} FROM service_principal_policies WHERE org_id = ? AND service_name = ? LIMIT 1`,
141
+ args: [orgId, serviceName],
142
+ });
143
+ return rows[0] ? mapRow(rows[0]) : null;
144
+ }
145
+ export async function listServicePrincipalPolicies(orgId) {
146
+ await ensureTable();
147
+ const { rows } = await getDbExec().execute({
148
+ sql: `SELECT ${COLUMNS} FROM service_principal_policies WHERE org_id = ? ORDER BY service_name`,
149
+ args: [orgId],
150
+ });
151
+ return rows.map(mapRow);
152
+ }
153
+ /**
154
+ * Create or update the governance fields. Fields left `undefined` keep their
155
+ * stored value; `null` clears one. Lifecycle changes go through
156
+ * `setServicePrincipalLifecycle` so they always carry an actor and a reason.
157
+ */
158
+ export async function upsertServicePrincipalPolicy(orgId, serviceName, input) {
159
+ const now = Date.now();
160
+ const updatedColumns = [
161
+ input.ownerEmail !== undefined
162
+ ? "owner_email = EXCLUDED.owner_email"
163
+ : null,
164
+ input.team !== undefined ? "team = EXCLUDED.team" : null,
165
+ input.riskTier !== undefined ? "risk_tier = EXCLUDED.risk_tier" : null,
166
+ input.purpose !== undefined ? "purpose = EXCLUDED.purpose" : null,
167
+ input.allowedActions !== undefined
168
+ ? "allowed_actions = EXCLUDED.allowed_actions"
169
+ : null,
170
+ "updated_at = EXCLUDED.updated_at",
171
+ ].filter((column) => column !== null);
172
+ const allowedJson = input.allowedActions == null ? null : JSON.stringify(input.allowedActions);
173
+ await getDbExec().execute({
174
+ sql: `INSERT INTO service_principal_policies (org_id, service_name, owner_email, team, risk_tier, purpose, lifecycle, allowed_actions, created_at, updated_at)
175
+ VALUES (?, ?, ?, ?, ?, ?, 'active', ?, ?, ?)
176
+ ON CONFLICT (org_id, service_name) DO UPDATE SET ${updatedColumns.join(", ")}`,
177
+ args: [
178
+ orgId,
179
+ serviceName,
180
+ input.ownerEmail ?? null,
181
+ input.team ?? null,
182
+ input.riskTier ?? "medium",
183
+ input.purpose ?? null,
184
+ allowedJson,
185
+ now,
186
+ now,
187
+ ],
188
+ });
189
+ const saved = await getServicePrincipalPolicy(orgId, serviceName);
190
+ if (!saved) {
191
+ throw new Error("Service principal policy was not readable after write.");
192
+ }
193
+ return saved;
194
+ }
195
+ export class ServicePrincipalRetiredError extends Error {
196
+ statusCode = 409;
197
+ constructor() {
198
+ super("A retired service principal cannot be resumed.");
199
+ this.name = "ServicePrincipalRetiredError";
200
+ }
201
+ }
202
+ export async function setServicePrincipalLifecycle(orgId, serviceName, lifecycle, change, db = getDbExec()) {
203
+ await ensureTable();
204
+ const now = Date.now();
205
+ // Upsert so a legacy principal with no record can be suspended in one step.
206
+ await db.execute({
207
+ sql: `INSERT INTO service_principal_policies (org_id, service_name, risk_tier, lifecycle, lifecycle_reason, lifecycle_changed_by, lifecycle_changed_at, created_at, updated_at) VALUES (?, ?, 'medium', ?, ?, ?, ?, ?, ?)
208
+ ON CONFLICT (org_id, service_name) DO UPDATE SET lifecycle = EXCLUDED.lifecycle, lifecycle_reason = EXCLUDED.lifecycle_reason, lifecycle_changed_by = EXCLUDED.lifecycle_changed_by, lifecycle_changed_at = EXCLUDED.lifecycle_changed_at, updated_at = EXCLUDED.updated_at
209
+ WHERE service_principal_policies.lifecycle != 'retired' OR EXCLUDED.lifecycle = 'retired'`,
210
+ args: [
211
+ orgId,
212
+ serviceName,
213
+ lifecycle,
214
+ change.reason ?? null,
215
+ change.actorEmail,
216
+ now,
217
+ now,
218
+ now,
219
+ ],
220
+ });
221
+ const saved = await getServicePrincipalPolicy(orgId, serviceName, db);
222
+ if (!saved) {
223
+ throw new Error("Service principal policy was not readable after write.");
224
+ }
225
+ if (saved.lifecycle === "retired" && lifecycle !== "retired") {
226
+ throw new ServicePrincipalRetiredError();
227
+ }
228
+ return saved;
229
+ }
230
+ /**
231
+ * Resolve whether a caller may run from its identity email and verified org.
232
+ * Non-service identities return `not-service` without touching the database,
233
+ * so the hot path for human callers is unchanged.
234
+ */
235
+ export async function evaluateServicePrincipal(email, orgId, db = getDbExec()) {
236
+ const parsed = parseServiceIdentityEmail(email);
237
+ if (!parsed)
238
+ return { status: "not-service" };
239
+ // The email alone is not proof of the org (see service-identity.ts); require
240
+ // a verified org claim before reading this principal's policy.
241
+ if (!orgId || orgId !== parsed.orgId)
242
+ return { status: "org-mismatch" };
243
+ try {
244
+ const policy = await getServicePrincipalPolicy(parsed.orgId, parsed.serviceName, db);
245
+ if (!policy) {
246
+ return {
247
+ status: "ungoverned",
248
+ orgId: parsed.orgId,
249
+ serviceName: parsed.serviceName,
250
+ };
251
+ }
252
+ return policy.lifecycle === "active"
253
+ ? { status: "active", policy }
254
+ : { status: policy.lifecycle, policy };
255
+ }
256
+ catch (error) {
257
+ console.error("[service-principal] Policy lookup failed:", error);
258
+ return { status: "unavailable" };
259
+ }
260
+ }
261
+ /** Whether the principal may call `actionName`. `null` grants everything. */
262
+ export function isActionGranted(allowedActions, actionName) {
263
+ if (allowedActions === null)
264
+ return true;
265
+ if (!allowedActions.every(isServicePrincipalActionPattern))
266
+ return false;
267
+ return allowedActions.some((pattern) => pattern.endsWith("*")
268
+ ? actionName.startsWith(pattern.slice(0, -1))
269
+ : pattern === actionName);
270
+ }
271
+ /** Message returned to a refused caller; never echoes the stored reason. */
272
+ export const SERVICE_PRINCIPAL_SUSPENDED_MESSAGE = "This service principal is suspended or retired. Ask an organization admin to resume it.";
@@ -75,9 +75,8 @@ export declare function createCustomProviderRegistrationAction<TSchema extends Z
75
75
  user: "user";
76
76
  }>>;
77
77
  }, z.core.$strip>>, {
78
- id?: undefined;
79
- message?: undefined;
80
78
  deleted?: undefined;
79
+ message?: undefined;
81
80
  found?: undefined;
82
81
  providers: {
83
82
  id: string;
@@ -91,20 +90,21 @@ export declare function createCustomProviderRegistrationAction<TSchema extends Z
91
90
  count: number;
92
91
  provider?: undefined;
93
92
  registered?: undefined;
93
+ id?: undefined;
94
94
  label?: undefined;
95
95
  } | {
96
- id?: undefined;
97
- message?: undefined;
98
96
  deleted?: undefined;
97
+ message?: undefined;
99
98
  providers?: undefined;
100
99
  count?: undefined;
101
100
  found: boolean;
102
101
  provider: import("../custom-registry.js").CustomProviderConfig;
103
102
  registered?: undefined;
103
+ id?: undefined;
104
104
  label?: undefined;
105
105
  } | {
106
- message?: undefined;
107
106
  deleted?: undefined;
107
+ message?: undefined;
108
108
  providers?: undefined;
109
109
  count?: undefined;
110
110
  provider?: undefined;
@@ -311,8 +311,10 @@ export declare function createProviderApiActions(runtime: Pick<ProviderApiRuntim
311
311
  notes?: string | undefined;
312
312
  scope?: "org" | "user" | undefined;
313
313
  }, {
314
- provider?: undefined;
315
314
  id?: undefined;
315
+ deleted?: undefined;
316
+ provider?: undefined;
317
+ message?: undefined;
316
318
  found?: undefined;
317
319
  providers: {
318
320
  id: string;
@@ -324,46 +326,44 @@ export declare function createProviderApiActions(runtime: Pick<ProviderApiRuntim
324
326
  updatedAt: number;
325
327
  }[];
326
328
  count: number;
327
- deleted?: undefined;
328
329
  registered?: undefined;
329
330
  label?: undefined;
330
- message?: undefined;
331
331
  } | {
332
332
  id?: undefined;
333
- count?: undefined;
333
+ deleted?: undefined;
334
+ message?: undefined;
334
335
  providers?: undefined;
336
+ count?: undefined;
335
337
  found: boolean;
336
338
  provider: import("../custom-registry.js").CustomProviderConfig;
337
- deleted?: undefined;
338
339
  registered?: undefined;
339
340
  label?: undefined;
340
- message?: undefined;
341
341
  } | {
342
+ deleted?: undefined;
342
343
  provider?: undefined;
343
- count?: undefined;
344
+ message?: undefined;
344
345
  providers?: undefined;
346
+ count?: undefined;
345
347
  found: boolean;
346
348
  id: string;
347
- deleted?: undefined;
348
349
  registered?: undefined;
349
350
  label?: undefined;
350
- message?: undefined;
351
351
  } | {
352
352
  provider?: undefined;
353
- count?: undefined;
353
+ message?: undefined;
354
354
  found?: undefined;
355
355
  providers?: undefined;
356
+ count?: undefined;
356
357
  deleted: boolean;
357
358
  id: string;
358
359
  registered?: undefined;
359
360
  label?: undefined;
360
- message?: undefined;
361
361
  } | {
362
+ deleted?: undefined;
362
363
  provider?: undefined;
363
- count?: undefined;
364
364
  found?: undefined;
365
365
  providers?: undefined;
366
- deleted?: undefined;
366
+ count?: undefined;
367
367
  registered: boolean;
368
368
  id: string;
369
369
  label: string;
@@ -2,6 +2,7 @@ import { createCodingToolRegistry } from "../../coding-tools/index.js";
2
2
  import { getRequestRunContext } from "../../server/request-context.js";
3
3
  import { normalizeDatabaseToolsMode, } from "../db/tool-mode.js";
4
4
  import { dbExecToolParameters } from "../db/tool-schemas.js";
5
+ import { serializeCliArgs } from "../parse-args.js";
5
6
  import { tool as listFilesTool, run as listFilesRun } from "./list-files.js";
6
7
  import { tool as readFileTool, run as readFileRun } from "./read-file.js";
7
8
  import { tool as searchFilesTool, run as searchFilesRun, } from "./search-files.js";
@@ -12,14 +13,7 @@ function wrapCliScript(tool, cliDefault, opts) {
12
13
  tool,
13
14
  ...(opts?.readOnly ? { readOnly: true } : {}),
14
15
  run: async (args) => {
15
- const cliArgs = [];
16
- for (const [k, v] of Object.entries(args)) {
17
- const raw = v;
18
- const value = raw != null && typeof raw === "object"
19
- ? JSON.stringify(raw)
20
- : String(raw);
21
- cliArgs.push(`--${k}`, value);
22
- }
16
+ const cliArgs = serializeCliArgs(args);
23
17
  const logs = [];
24
18
  const origLog = console.log;
25
19
  console.log = (...a) => {
@@ -1,2 +1,9 @@
1
1
  export declare function parseArgs(args: string[]): Record<string, string>;
2
+ export declare function serializeCliArgs(args: Record<string, unknown>): string[];
3
+ type NormalizeShellArgsOptions = {
4
+ backslashEscapes?: boolean;
5
+ splitAllWhitespace?: boolean;
6
+ };
7
+ export declare function normalizeShellArgs(input: string, options?: NormalizeShellArgsOptions): string[];
2
8
  export declare function camelCaseArgs(args: Record<string, string>): Record<string, string>;
9
+ export {};
@@ -23,6 +23,82 @@ export function parseArgs(args) {
23
23
  }
24
24
  return result;
25
25
  }
26
+ export function serializeCliArgs(args) {
27
+ return Object.entries(args).flatMap(([key, raw]) => {
28
+ const value = raw != null && typeof raw === "object"
29
+ ? JSON.stringify(raw)
30
+ : String(raw);
31
+ return value.startsWith("--") ? [`--${key}=${value}`] : [`--${key}`, value];
32
+ });
33
+ }
34
+ export function normalizeShellArgs(input, options = {}) {
35
+ const tokens = [];
36
+ let current = "";
37
+ let inDouble = false;
38
+ let inSingle = false;
39
+ let wasQuoted = false;
40
+ let escape = false;
41
+ const pushToken = () => {
42
+ tokens.push({ value: current, quoted: wasQuoted });
43
+ current = "";
44
+ wasQuoted = false;
45
+ };
46
+ for (let i = 0; i < input.length; i++) {
47
+ const ch = input[i];
48
+ if (escape) {
49
+ current += ch;
50
+ escape = false;
51
+ continue;
52
+ }
53
+ if (options.backslashEscapes && ch === "\\") {
54
+ if (inSingle) {
55
+ current += ch;
56
+ }
57
+ else {
58
+ escape = true;
59
+ }
60
+ continue;
61
+ }
62
+ if (ch === "'" && !inDouble) {
63
+ inSingle = !inSingle;
64
+ wasQuoted = true;
65
+ continue;
66
+ }
67
+ if (ch === '"' && !inSingle) {
68
+ inDouble = !inDouble;
69
+ wasQuoted = true;
70
+ continue;
71
+ }
72
+ if ((options.splitAllWhitespace
73
+ ? /\s/.test(ch)
74
+ : ch === " " || ch === "\t") &&
75
+ !inSingle &&
76
+ !inDouble) {
77
+ if (current.length > 0 || wasQuoted)
78
+ pushToken();
79
+ continue;
80
+ }
81
+ current += ch;
82
+ }
83
+ if (current.length > 0 || wasQuoted)
84
+ pushToken();
85
+ const normalized = [];
86
+ for (let i = 0; i < tokens.length; i++) {
87
+ const token = tokens[i];
88
+ const next = tokens[i + 1];
89
+ if (token.value.startsWith("--") &&
90
+ !token.value.includes("=") &&
91
+ next?.quoted &&
92
+ next.value.startsWith("--")) {
93
+ normalized.push(`${token.value}=${next.value}`);
94
+ i++;
95
+ }
96
+ else {
97
+ normalized.push(token.value);
98
+ }
99
+ }
100
+ return normalized;
101
+ }
26
102
  export function camelCaseArgs(args) {
27
103
  const result = {};
28
104
  for (const [key, value] of Object.entries(args)) {
@@ -1,6 +1,6 @@
1
1
  export { findWorkspaceRoot } from "./workspace-root.js";
2
2
  export { fail, type FailOptions } from "../action.js";
3
- export { parseArgs, camelCaseArgs } from "./parse-args.js";
3
+ export { parseArgs, serializeCliArgs, camelCaseArgs } from "./parse-args.js";
4
4
  export declare function loadEnv(envPath?: string): void;
5
5
  export declare function isValidPath(p: string): boolean;
6
6
  export declare function isValidProjectPath(project: string): boolean;
@@ -4,7 +4,7 @@ import dotenv from "dotenv";
4
4
  import { findWorkspaceRoot } from "./workspace-root.js";
5
5
  export { findWorkspaceRoot } from "./workspace-root.js";
6
6
  export { fail } from "../action.js";
7
- export { parseArgs, camelCaseArgs } from "./parse-args.js";
7
+ export { parseArgs, serializeCliArgs, camelCaseArgs } from "./parse-args.js";
8
8
  export function loadEnv(envPath) {
9
9
  const appEnv = envPath ?? path.join(process.cwd(), ".env");
10
10
  const shellKeys = new Set(Object.keys(process.env));
@@ -58,47 +58,47 @@ export declare function createWriteSecretHandler(): import("h3").EventHandlerWit
58
58
  error: string;
59
59
  errorCode?: undefined;
60
60
  retryable?: undefined;
61
- ok?: undefined;
62
61
  status?: undefined;
62
+ ok?: undefined;
63
63
  } | {
64
64
  error: string;
65
65
  errorCode: string;
66
66
  retryable?: undefined;
67
- ok?: undefined;
68
67
  status?: undefined;
68
+ ok?: undefined;
69
69
  } | {
70
70
  error: string;
71
71
  errorCode: string;
72
72
  retryable: boolean;
73
- ok?: undefined;
74
73
  status?: undefined;
74
+ ok?: undefined;
75
75
  } | {
76
- error?: undefined;
77
76
  errorCode?: undefined;
78
77
  retryable?: undefined;
79
78
  ok: boolean;
80
79
  status: string;
80
+ error?: undefined;
81
81
  } | {
82
- ok?: undefined;
83
82
  error: string;
84
83
  removed?: undefined;
84
+ ok?: undefined;
85
85
  } | {
86
- error?: undefined;
87
86
  ok: boolean;
88
87
  removed: boolean;
88
+ error?: undefined;
89
89
  }>>;
90
90
  export declare function createTestSecretHandler(): import("h3").EventHandlerWithFetch<import("h3").EventHandlerRequest, Promise<{
91
- ok?: undefined;
92
91
  error: string;
93
92
  note?: undefined;
93
+ ok?: undefined;
94
94
  } | {
95
- error?: undefined;
96
95
  ok: boolean;
97
96
  note?: undefined;
98
- } | {
99
97
  error?: undefined;
98
+ } | {
100
99
  ok: boolean;
101
100
  note: string;
101
+ error?: undefined;
102
102
  } | {
103
103
  note?: undefined;
104
104
  ok: boolean;
@@ -126,11 +126,11 @@ export declare function createAdHocSecretHandler(): import("h3").EventHandlerWit
126
126
  } | {
127
127
  error: string;
128
128
  } | {
129
- error?: undefined;
130
129
  ok: boolean;
131
130
  key: string;
132
- } | {
133
131
  error?: undefined;
132
+ } | {
134
133
  ok: boolean;
135
134
  removed: boolean;
135
+ error?: undefined;
136
136
  }>>;
@@ -1,6 +1,7 @@
1
1
  import nodePath from "node:path";
2
2
  import "../authorization/check-action.js";
3
3
  import { CORE_ACTION_GROUPS } from "../framework-tools.js";
4
+ import { normalizeShellArgs, serializeCliArgs } from "../scripts/parse-args.js";
4
5
  import { captureCliOutput } from "./cli-capture.js";
5
6
  let _fs;
6
7
  async function getFs() {
@@ -54,39 +55,6 @@ export function mergePackageActions(registry) {
54
55
  registry[name] = entry;
55
56
  }
56
57
  }
57
- function splitShellArgs(input) {
58
- const tokens = [];
59
- let current = "";
60
- let inDouble = false;
61
- let inSingle = false;
62
- let wasQuoted = false;
63
- for (let i = 0; i < input.length; i++) {
64
- const ch = input[i];
65
- if (ch === '"' && !inSingle) {
66
- inDouble = !inDouble;
67
- wasQuoted = true;
68
- continue;
69
- }
70
- if (ch === "'" && !inDouble) {
71
- inSingle = !inSingle;
72
- wasQuoted = true;
73
- continue;
74
- }
75
- if ((ch === " " || ch === "\t") && !inDouble && !inSingle) {
76
- if (current.length > 0 || wasQuoted) {
77
- tokens.push(current);
78
- }
79
- current = "";
80
- wasQuoted = false;
81
- continue;
82
- }
83
- current += ch;
84
- }
85
- if (current.length > 0 || wasQuoted) {
86
- tokens.push(current);
87
- }
88
- return tokens;
89
- }
90
58
  function wrapDefaultExport(name, defaultFn) {
91
59
  const tool = {
92
60
  description: `Run the "${name}" action. Pass arguments as key-value pairs.`,
@@ -103,15 +71,9 @@ function wrapDefaultExport(name, defaultFn) {
103
71
  return {
104
72
  tool,
105
73
  run: async (args) => {
106
- const cliArgs = [];
107
- if (args.args && Object.keys(args).length === 1) {
108
- cliArgs.push(...splitShellArgs(args.args));
109
- }
110
- else {
111
- for (const [k, v] of Object.entries(args)) {
112
- cliArgs.push(`--${k}`, v);
113
- }
114
- }
74
+ const cliArgs = args.args && Object.keys(args).length === 1
75
+ ? normalizeShellArgs(args.args)
76
+ : serializeCliArgs(args);
115
77
  return captureCliOutput(() => defaultFn(cliArgs));
116
78
  },
117
79
  };
@@ -772,6 +734,10 @@ export async function mergeCoreSharingActions(registry) {
772
734
  "export-audit-events",
773
735
  () => import("../audit/actions/export-audit-events.js"),
774
736
  ],
737
+ [
738
+ "export-audit-ocsf",
739
+ () => import("../audit/actions/export-audit-ocsf.js"),
740
+ ],
775
741
  [
776
742
  "export-resource-pack",
777
743
  () => import("../resources/actions/export-resource-pack.js"),
@@ -908,6 +874,14 @@ export async function mergeCoreSharingActions(registry) {
908
874
  "revoke-org-service-token",
909
875
  () => import("../mcp/actions/revoke-org-service-token.js"),
910
876
  ],
877
+ [
878
+ "set-service-principal-policy",
879
+ () => import("../mcp/actions/set-service-principal-policy.js"),
880
+ ],
881
+ [
882
+ "set-service-principal-lifecycle",
883
+ () => import("../mcp/actions/set-service-principal-lifecycle.js"),
884
+ ],
911
885
  ["list-mcp-tools", () => import("../mcp/actions/list-mcp-tools.js")],
912
886
  ["call-mcp-tool", () => import("../mcp/actions/call-mcp-tool.js")],
913
887
  ];