@aiwg/cli 2026.8.20 → 2026.8.26

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/bin/aiwg.mjs CHANGED
@@ -203,13 +203,6 @@ function maybeHandleFastHelp(args) {
203
203
  return true;
204
204
  }
205
205
 
206
- if (maybeHandleFastVersion(process.argv.slice(2))) {
207
- process.exit(0);
208
- }
209
- if (maybeHandleFastHelp(process.argv.slice(2))) {
210
- process.exit(0);
211
- }
212
-
213
206
  // Preflight: verify dist/ is built before any of the dynamic imports below
214
207
  // try to resolve files that don't exist. Without this, a missing/incomplete
215
208
  // dist/ surfaces as a raw `Cannot find module '.../dist/src/.../*.mjs'` from
@@ -240,6 +233,31 @@ function maybeWarnUnbuiltDist() {
240
233
  }
241
234
  maybeWarnUnbuiltDist();
242
235
 
236
+ // Display a cached notice and schedule its refresh before every eligible CLI
237
+ // path, including fast help/version, channel recovery, and later preflight
238
+ // failures. This local-only bootstrap never waits on the registry and failures
239
+ // are deliberately ignored so update advice cannot change command behavior.
240
+ async function runUpdateNotifierBootstrap() {
241
+ try {
242
+ const notifierPath = path.join(packageRoot, 'dist', 'src', 'update', 'notifier.mjs');
243
+ const notifier = await import(pathToFileURL(notifierPath).href);
244
+ const activePackageRoot = notifier.resolveActivePackageRoot(packageRoot);
245
+ notifier.maybePrintNotice(activePackageRoot);
246
+ notifier.scheduleBackgroundCheck(activePackageRoot);
247
+ } catch {
248
+ // Best effort: installation/router diagnostics retain authority.
249
+ }
250
+ }
251
+
252
+ await runUpdateNotifierBootstrap();
253
+
254
+ if (maybeHandleFastVersion(process.argv.slice(2))) {
255
+ process.exit(0);
256
+ }
257
+ if (maybeHandleFastHelp(process.argv.slice(2))) {
258
+ process.exit(0);
259
+ }
260
+
243
261
  // Mint or inherit an invocation ID before anything else loads. Child processes
244
262
  // spawned by handlers (detached update-notifier, aiwg exec, etc.) inherit the
245
263
  // parent's ID via `AIWG_INVOCATION_ID` so their JSONL log records correlate
@@ -398,15 +416,6 @@ async function main() {
398
416
  // tag every record with it.
399
417
  await applyVerbosityFromArgs(args, routerPath);
400
418
 
401
- // Update notifier: print any pending notice from the previous run's
402
- // background check, then schedule the next background check. Both are
403
- // non-blocking — the current command never waits on the network.
404
- // Honors NO_UPDATE_NOTIFIER, CI=*, and non-TTY stderr.
405
- const notifierPath = path.join(activePackageRoot, 'dist', 'src', 'update', 'notifier.mjs');
406
- const { scheduleBackgroundCheck, maybePrintNotice } = await import(pathToFileURL(notifierPath).href);
407
- maybePrintNotice(activePackageRoot);
408
- scheduleBackgroundCheck(activePackageRoot);
409
-
410
419
  // Top-level cancellation controller. SIGINT / SIGTERM flip it, long-running
411
420
  // handlers plumb ctx.signal through fetches and loops so Ctrl-C cancels
412
421
  // in-flight work cleanly instead of leaving orphaned sockets and children.
@@ -9,6 +9,7 @@ export { run } from '../cli/router.js';
9
9
  export * from '../resources/index.js';
10
10
  export * from '../sessions/index.js';
11
11
  export * from '../memory/index.js';
12
+ export * from '../storage/index.js';
12
13
  export * from '../security/threat-assessment-config.js';
13
14
  export * from '../security/artifact-verifier.js';
14
15
  export * from '../security/artifact-attestation.js';
@@ -9,6 +9,7 @@ export { run } from '../cli/router.js';
9
9
  export * from '../resources/index.js';
10
10
  export * from '../sessions/index.js';
11
11
  export * from '../memory/index.js';
12
+ export * from '../storage/index.js';
12
13
  export * from '../security/threat-assessment-config.js';
13
14
  export * from '../security/artifact-verifier.js';
14
15
  export * from '../security/artifact-attestation.js';
@@ -102,6 +102,9 @@ export const artifactVerifyHandler = {
102
102
  description: 'Verify cross-asset DSSE provenance and manage trust roots',
103
103
  category: 'utility',
104
104
  aliases: [],
105
+ async help() {
106
+ return { exitCode: 0, message: usage() };
107
+ },
105
108
  async execute(ctx) {
106
109
  if (ctx.args.includes('--help') || ctx.args.includes('-h') || ctx.args.length === 0)
107
110
  return { exitCode: 0, message: usage() };
@@ -10,7 +10,7 @@
10
10
  *
11
11
  * @implements @agentic/code/frameworks/sdlc-complete/rules/self-maintenance.md
12
12
  * @source @src/cli/router.ts
13
- * @issue #482, #557, #694
13
+ * @issue #173, #174, #482, #557, #694
14
14
  */
15
15
  import { promises as fs } from 'fs';
16
16
  import path from 'path';
@@ -151,6 +151,30 @@ export function collectModelDeployArgs(args) {
151
151
  }
152
152
  return forwarded;
153
153
  }
154
+ const REFRESH_HELP = `Usage: aiwg refresh [options]
155
+
156
+ Update AIWG, re-deploy installed frameworks, and run health verification.
157
+
158
+ Options:
159
+ --dry-run Preview changes without updating or deploying
160
+ --quiet Suppress progress output
161
+ --skip-update Skip the installation update
162
+ --packages-only Refresh remote packages only
163
+ --provider <name> Override provider auto-detection
164
+ --channel <name> Select the update channel (stable or main)
165
+ --frameworks <list> Re-deploy a comma-separated installed subset
166
+ --model <name> Override all deployed agent model tiers
167
+ --reasoning-model <name> Override the reasoning model tier
168
+ --coding-model <name> Override the coding model tier
169
+ --efficiency-model <name> Override the efficiency model tier
170
+ --filter <pattern> Limit model deployment by agent name
171
+ --filter-role <role> Limit model deployment by role
172
+ --model-tier <tier> Limit model deployment by tier
173
+ --save Save model overrides to the project
174
+ --save-user Save model overrides to user configuration
175
+ -h, --help Show this help without running refresh
176
+
177
+ Alias: aiwg sync (deprecated)`;
154
178
  /**
155
179
  * Refresh command handler (formerly sync)
156
180
  */
@@ -160,6 +184,9 @@ export const refreshHandler = {
160
184
  description: 'Refresh AIWG to latest version and re-deploy installed frameworks',
161
185
  category: 'maintenance',
162
186
  aliases: ['--refresh', 'sync', '--sync'],
187
+ async help() {
188
+ return { exitCode: 0, message: REFRESH_HELP, rawOutput: true };
189
+ },
163
190
  async execute(ctx) {
164
191
  const dryRun = hasFlag(ctx.args, '--dry-run');
165
192
  const quiet = hasFlag(ctx.args, '--quiet');
@@ -451,6 +478,10 @@ export const refreshHandler = {
451
478
  if (dryRun) {
452
479
  ui.info('Dry run complete — no changes made');
453
480
  }
481
+ else if (updateFailure) {
482
+ ui.warn(`Refresh completed with installation update failure (exit ${updateFailure.exitCode}); ` +
483
+ 're-deployment continued, but AIWG may still be on the previous version.');
484
+ }
454
485
  else {
455
486
  ui.success('Refresh complete');
456
487
  }
@@ -459,7 +490,11 @@ export const refreshHandler = {
459
490
  // Quiet mode: JSON output
460
491
  if (quiet) {
461
492
  const output = JSON.stringify({
462
- status: dryRun ? 'dry-run' : 'refreshed',
493
+ status: dryRun
494
+ ? 'dry-run'
495
+ : updateFailure
496
+ ? 'refreshed-with-update-failure'
497
+ : 'refreshed',
463
498
  provider: detectedProvider,
464
499
  frameworks,
465
500
  skipUpdate,
@@ -1139,34 +1139,40 @@ export const pluginStatusHandler = {
1139
1139
  *
1140
1140
  * Delegates to tools/plugin/package-plugins.mjs
1141
1141
  */
1142
+ function packagePluginHelp() {
1143
+ return {
1144
+ exitCode: 0,
1145
+ message: [
1146
+ "aiwg package-plugin — package a project-local or built-in marketplace wrapper",
1147
+ "",
1148
+ "Usage:",
1149
+ " aiwg package-plugin <name> [--source <path>] [--output <path>] [--provider <name>] [--clean] [--dry-run]",
1150
+ " aiwg package-plugin --plugin <name> [options] # compatibility form",
1151
+ "",
1152
+ "Options:",
1153
+ " --source <path> explicit project-local wrapper source (must stay inside the project)",
1154
+ " --output <path> standalone archive output (default: dist/plugins)",
1155
+ " --provider <name> claude, codex, or all for standalone wrappers; built-ins retain all formats",
1156
+ " --clean clean generated plugin output before packaging",
1157
+ " --dry-run, -n preview without writing",
1158
+ " --help, -h show this help",
1159
+ "",
1160
+ "Project-local wrappers are discovered under .aiwg/plugins and packaged as deterministic archives.",
1161
+ ].join("\n"),
1162
+ };
1163
+ }
1142
1164
  export const packagePluginHandler = {
1143
1165
  id: "package-plugin",
1144
1166
  name: "Package Plugin",
1145
1167
  description: "Package a plugin for distribution",
1146
1168
  category: "plugin",
1147
1169
  aliases: ["-package-plugin", "--package-plugin"],
1170
+ async help() {
1171
+ return packagePluginHelp();
1172
+ },
1148
1173
  async execute(ctx) {
1149
1174
  if (ctx.args.includes("--help") || ctx.args.includes("-h")) {
1150
- return {
1151
- exitCode: 0,
1152
- message: [
1153
- "aiwg package-plugin — package a project-local or built-in marketplace wrapper",
1154
- "",
1155
- "Usage:",
1156
- " aiwg package-plugin <name> [--source <path>] [--output <path>] [--provider <name>] [--clean] [--dry-run]",
1157
- " aiwg package-plugin --plugin <name> [options] # compatibility form",
1158
- "",
1159
- "Options:",
1160
- " --source <path> explicit project-local wrapper source (must stay inside the project)",
1161
- " --output <path> standalone archive output (default: dist/plugins)",
1162
- " --provider <name> claude, codex, or all for standalone wrappers; built-ins retain all formats",
1163
- " --clean clean generated plugin output before packaging",
1164
- " --dry-run, -n preview without writing",
1165
- " --help, -h show this help",
1166
- "",
1167
- "Project-local wrappers are discovered under .aiwg/plugins and packaged as deterministic archives.",
1168
- ].join("\n"),
1169
- };
1175
+ return packagePluginHelp();
1170
1176
  }
1171
1177
  const hasExplicitPlugin = ctx.args.includes("--plugin") || ctx.args.includes("-p");
1172
1178
  const positional = ctx.args[0] && !ctx.args[0].startsWith("-")
@@ -466,6 +466,9 @@ export const doctorHandler = {
466
466
  description: 'Run health diagnostics',
467
467
  category: 'maintenance',
468
468
  aliases: ['-doctor', '--doctor'],
469
+ async help() {
470
+ return { exitCode: 0, message: DOCTOR_HELP, rawOutput: true };
471
+ },
469
472
  async execute(ctx) {
470
473
  if (ctx.args.includes('--help') || ctx.args.includes('-h')) {
471
474
  return { exitCode: 0, message: DOCTOR_HELP, rawOutput: true };
@@ -10,6 +10,7 @@
10
10
  * @tests @test/unit/cli/router.test.ts
11
11
  * @issue #33
12
12
  * @issue #58
13
+ * @issue #174
13
14
  */
14
15
  import { loadRegistry } from '../extensions/loader.js';
15
16
  import { getFrameworkRoot } from '../channel/manager.mjs';
@@ -134,6 +135,32 @@ export async function run(args, options = {}) {
134
135
  ui.error(`No handler found for command: ${commandId}`);
135
136
  process.exit(1);
136
137
  }
138
+ // Help must be intercepted before hooks and the normal handler path. Some
139
+ // commands mutate project or installation state, so passing an unrecognised
140
+ // help flag through to execute() is unsafe (#174).
141
+ if (commandArgs.includes('--help') || commandArgs.includes('-h')) {
142
+ const ctx = await buildContext(commandArgs, args, options);
143
+ if (handler.help) {
144
+ const result = await handler.help(ctx);
145
+ if (result.message) {
146
+ if (result.rawOutput) {
147
+ process.stdout.write(result.message.endsWith('\n') ? result.message : `${result.message}\n`);
148
+ }
149
+ else if (result.exitCode !== 0) {
150
+ ui.error(result.message);
151
+ }
152
+ else {
153
+ ui.info(result.message);
154
+ }
155
+ }
156
+ if (result.exitCode !== 0)
157
+ process.exit(result.exitCode);
158
+ }
159
+ else {
160
+ ui.info(`No detailed help for \`aiwg ${commandId}\`. Run \`aiwg help\` for the command overview.`);
161
+ }
162
+ return;
163
+ }
137
164
  // Build context for handler and hooks
138
165
  const ctx = await buildContext(commandArgs, args, options);
139
166
  // Build hook context
@@ -42,6 +42,17 @@ export const FEATURE_CATALOG = [
42
42
  ],
43
43
  cost: '~5 MB — native compile via node-gyp',
44
44
  },
45
+ {
46
+ name: 'postgres',
47
+ description: 'Advanced direct PostgreSQL canonical-storage backend',
48
+ packages: ['pg'],
49
+ packageSpecs: { pg: '8.23.0' },
50
+ enables: [
51
+ 'aiwg.storage-backend/v1 direct PostgreSQL persistence',
52
+ 'transactional migration batches, snapshots, cursors, and tombstones',
53
+ ],
54
+ cost: '~1 MB — pure JavaScript PostgreSQL client',
55
+ },
45
56
  {
46
57
  name: 'pty',
47
58
  description: 'PTY bridge for daemon — pass-through interactive TUIs (Claude Code, Codex, etc.)',
@@ -21,11 +21,20 @@ export function resolveManagerCommand(file, args, options = {}) {
21
21
  return {
22
22
  file: commandInterpreter,
23
23
  args: ['/d', '/s', '/c', command],
24
+ // The payload above already contains cmd.exe-native quoting. Instruct
25
+ // Node not to apply MSVCRT escaping to those embedded quotes (#173).
26
+ windowsVerbatimArguments: true,
24
27
  };
25
28
  }
26
29
 
27
30
  export function executeManagerCommand(file, args, options = {}) {
28
31
  const invocation = resolveManagerCommand(file, args, options);
29
32
  const execute = options.execute ?? execFileSync;
30
- return execute(invocation.file, invocation.args, options.execOptions ?? { stdio: 'inherit' });
33
+ const execOptions = options.execOptions
34
+ ? { ...options.execOptions }
35
+ : { stdio: 'inherit' };
36
+ if (invocation.windowsVerbatimArguments === true) {
37
+ execOptions.windowsVerbatimArguments = true;
38
+ }
39
+ return execute(invocation.file, invocation.args, execOptions);
31
40
  }
@@ -43,8 +43,17 @@ export const STORAGE_BACKEND_MATRIX = {
43
43
  sqlite: descriptor('sqlite', 'supported', [...BASELINE, 'atomic-batch', 'consistent-snapshot', 'tombstones', 'idempotency-keys', 'filtered-query', 'cursor-pagination', 'backup', 'restore'], 'wal', 'single-host', 'serializable', 'regenerable-index'),
44
44
  'fortemi-core-static': descriptor('fortemi-core-static', 'supported', ['read', 'filtered-query', 'recursive-traversal', 'set-operations'], 'filesystem', 'single-host', 'snapshot', 'static-cache'),
45
45
  'fortemi-server': descriptor('fortemi-server', 'alpha', [...BASELINE, 'filtered-query', 'health', 'tls', 'tenant-isolation'], 'replicated', 'remote-service', 'none', 'remote-persistence'),
46
- 'postgres-direct': descriptor('postgres-direct', 'advanced', [], 'replicated', 'remote-service', 'none', 'canonical'),
47
- 'postgres-postgrest': descriptor('postgres-postgrest', 'advanced', [], 'replicated', 'remote-service', 'none', 'canonical'),
46
+ 'postgres-direct': descriptor('postgres-direct', 'advanced', [
47
+ ...BASELINE, 'atomic-batch', 'consistent-snapshot', 'change-cursor',
48
+ 'tombstones', 'idempotency-keys', 'recursive-traversal', 'set-operations',
49
+ 'filtered-query', 'cursor-pagination', 'health', 'readiness', 'telemetry',
50
+ 'tenant-isolation', 'tls',
51
+ ], 'replicated', 'remote-service', 'serializable', 'canonical'),
52
+ 'postgres-postgrest': descriptor('postgres-postgrest', 'advanced', [
53
+ ...BASELINE, 'atomic-batch', 'consistent-snapshot', 'change-cursor',
54
+ 'tombstones', 'idempotency-keys', 'filtered-query', 'cursor-pagination',
55
+ 'health', 'readiness', 'tenant-isolation', 'tls',
56
+ ], 'replicated', 'remote-service', 'serializable', 'canonical'),
48
57
  mysql: descriptor('mysql', 'deferred', [], 'replicated', 'remote-service', 'none', 'canonical'),
49
58
  };
50
59
  function descriptor(backend, maturity, capabilities, durability, availability, isolation, dataClass) {
@@ -0,0 +1,144 @@
1
+ export const POSTGRES_SCHEMA_VERSION = 1;
2
+ export const POSTGRES_SCHEMA_V1_SQL = `
3
+ CREATE TABLE IF NOT EXISTS aiwg_storage_schema (
4
+ singleton boolean PRIMARY KEY DEFAULT true CHECK (singleton),
5
+ schema_version integer NOT NULL
6
+ );
7
+ INSERT INTO aiwg_storage_schema(singleton, schema_version)
8
+ VALUES (true, 1) ON CONFLICT (singleton) DO NOTHING;
9
+ CREATE TABLE IF NOT EXISTS aiwg_storage_records (
10
+ tenant text NOT NULL,
11
+ subsystem text NOT NULL,
12
+ path text NOT NULL,
13
+ source_revision text NOT NULL,
14
+ digest text NOT NULL,
15
+ value jsonb,
16
+ tombstone boolean NOT NULL DEFAULT false,
17
+ deleted_at timestamptz,
18
+ delete_reason text,
19
+ idempotency_key text NOT NULL,
20
+ change_seq bigserial NOT NULL,
21
+ updated_at timestamptz NOT NULL DEFAULT clock_timestamp(),
22
+ PRIMARY KEY (tenant, subsystem, path),
23
+ UNIQUE (tenant, subsystem, idempotency_key)
24
+ );
25
+ CREATE INDEX IF NOT EXISTS aiwg_storage_records_change
26
+ ON aiwg_storage_records(tenant, subsystem, change_seq, path);
27
+ CREATE TABLE IF NOT EXISTS aiwg_storage_batch_receipts (
28
+ tenant text NOT NULL,
29
+ subsystem text NOT NULL,
30
+ batch_id uuid NOT NULL,
31
+ payload_digest text NOT NULL,
32
+ high_water_mark bigint NOT NULL,
33
+ receipt jsonb NOT NULL,
34
+ committed_at timestamptz NOT NULL DEFAULT clock_timestamp(),
35
+ PRIMARY KEY (tenant, subsystem, batch_id)
36
+ );
37
+ CREATE TABLE IF NOT EXISTS aiwg_storage_edges (
38
+ tenant text NOT NULL,
39
+ subsystem text NOT NULL,
40
+ source_path text NOT NULL,
41
+ target_path text NOT NULL,
42
+ edge_type text NOT NULL,
43
+ PRIMARY KEY (tenant, subsystem, source_path, target_path, edge_type)
44
+ );
45
+ CREATE INDEX IF NOT EXISTS aiwg_storage_edges_target
46
+ ON aiwg_storage_edges(tenant, subsystem, target_path, edge_type);
47
+ `;
48
+ const POSTGREST_FUNCTIONS = [
49
+ 'aiwg_reload_schema_v1()',
50
+ 'aiwg_health_v1(text,text)',
51
+ 'aiwg_changes_v1(text,text,bigint,integer)',
52
+ 'aiwg_snapshot_v1(text,text,integer)',
53
+ 'aiwg_query_records_v1(text,text,jsonb,text,integer,boolean)',
54
+ 'aiwg_get_record_v1(text,text,text)',
55
+ 'aiwg_commit_batch_v1(text,text,uuid,text,jsonb)',
56
+ 'aiwg_record_v1(aiwg_storage_records)',
57
+ ];
58
+ export async function inspectPostgresSchema(client) {
59
+ const exists = await client.query("SELECT to_regclass('public.aiwg_storage_schema') IS NOT NULL AS exists");
60
+ if (!exists.rows[0]?.exists)
61
+ return { version: 0, records: 0, receipts: 0, edges: 0 };
62
+ const result = await client.query(`
63
+ SELECT schema_version,
64
+ (SELECT count(*) FROM aiwg_storage_records) AS records,
65
+ (SELECT count(*) FROM aiwg_storage_batch_receipts) AS receipts,
66
+ (SELECT count(*) FROM aiwg_storage_edges) AS edges
67
+ FROM aiwg_storage_schema WHERE singleton=true`);
68
+ const row = result.rows[0];
69
+ if (Number(row?.schema_version) !== 1)
70
+ throw new Error(`unsupported PostgreSQL storage schema ${String(row?.schema_version)}`);
71
+ return { version: 1, records: Number(row.records), receipts: Number(row.receipts), edges: Number(row.edges) };
72
+ }
73
+ /** Upgrade an empty database to v1 under a dedicated migration-role connection. */
74
+ export async function upgradePostgresSchemaV1(client) {
75
+ await client.query('BEGIN');
76
+ try {
77
+ await client.query("SELECT pg_advisory_xact_lock(hashtextextended('aiwg-storage-schema',0))");
78
+ const before = await inspectPostgresSchema(client);
79
+ if (before.version === 1) {
80
+ await client.query('COMMIT');
81
+ return before;
82
+ }
83
+ await client.query(POSTGRES_SCHEMA_V1_SQL);
84
+ const after = await inspectPostgresSchema(client);
85
+ if (after.version !== 1)
86
+ throw new Error('PostgreSQL schema upgrade did not reach v1');
87
+ await client.query('COMMIT');
88
+ return after;
89
+ }
90
+ catch (error) {
91
+ try {
92
+ await client.query('ROLLBACK');
93
+ }
94
+ catch { /* preserve upgrade error */ }
95
+ throw error;
96
+ }
97
+ }
98
+ /**
99
+ * Roll v1 back to an empty schema. This is intentionally impossible without
100
+ * exact version and observed-count acknowledgements from a prior inspection.
101
+ */
102
+ export async function rollbackPostgresSchemaV1(client, approval) {
103
+ if (approval.expectedVersion !== 1 || approval.allowDataLoss !== true)
104
+ throw new Error('explicit v1 data-loss approval is required');
105
+ await client.query('BEGIN');
106
+ try {
107
+ await client.query("SELECT pg_advisory_xact_lock(hashtextextended('aiwg-storage-schema',0))");
108
+ const before = await inspectPostgresSchema(client);
109
+ const observed = { records: before.records, receipts: before.receipts, edges: before.edges };
110
+ if (before.version !== 1 || JSON.stringify(observed) !== JSON.stringify(approval.expectedCounts)) {
111
+ throw new Error('schema rollback approval does not match current version/counts');
112
+ }
113
+ for (const signature of POSTGREST_FUNCTIONS)
114
+ await client.query(`DROP FUNCTION IF EXISTS ${signature}`);
115
+ await client.query('DROP TABLE aiwg_storage_edges, aiwg_storage_batch_receipts, aiwg_storage_records, aiwg_storage_schema');
116
+ const after = await inspectPostgresSchema(client);
117
+ if (after.version !== 0)
118
+ throw new Error('PostgreSQL schema rollback did not reach v0');
119
+ await client.query('COMMIT');
120
+ return after;
121
+ }
122
+ catch (error) {
123
+ try {
124
+ await client.query('ROLLBACK');
125
+ }
126
+ catch { /* preserve rollback error */ }
127
+ throw error;
128
+ }
129
+ }
130
+ export function postgresLeastPrivilegeSql(runtimeRole) {
131
+ const role = quoteIdentifier(runtimeRole);
132
+ return `REVOKE ALL ON aiwg_storage_records, aiwg_storage_batch_receipts, aiwg_storage_edges FROM PUBLIC;
133
+ REVOKE ALL ON aiwg_storage_schema FROM PUBLIC;
134
+ GRANT USAGE ON SCHEMA public TO ${role};
135
+ GRANT SELECT, INSERT, UPDATE, DELETE ON aiwg_storage_records, aiwg_storage_batch_receipts, aiwg_storage_edges TO ${role};
136
+ GRANT SELECT ON aiwg_storage_schema TO ${role};
137
+ GRANT USAGE, SELECT ON SEQUENCE aiwg_storage_records_change_seq_seq TO ${role};`;
138
+ }
139
+ function quoteIdentifier(value) {
140
+ if (!value || value.length > 63 || /[\u0000-\u001f]/.test(value))
141
+ throw new Error('runtime role must be a printable PostgreSQL identifier up to 63 characters');
142
+ return `"${value.replaceAll('"', '""')}"`;
143
+ }
144
+ //# sourceMappingURL=postgres-schema.js.map