@formigio/fazemos-cli 0.10.41 → 0.10.43
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/dist/index.js +72 -6
- package/dist/index.js.map +1 -1
- package/package.json +1 -1
package/dist/index.js
CHANGED
|
@@ -7959,20 +7959,34 @@ apiKeys
|
|
|
7959
7959
|
.requiredOption('-n, --name <name>', 'Key name (e.g., joe-orchestrator)')
|
|
7960
7960
|
.requiredOption('-m, --member <id>', 'Member ID to bind the key to')
|
|
7961
7961
|
.option('-d, --description <desc>', 'Description')
|
|
7962
|
+
.option('--scope <scope>', 'Key scope: org (cross-project, owner/admin only) or project (default, today\'s behavior). --scope org mints a key that works across all projects the member belongs to; active project is ignored.', 'project')
|
|
7962
7963
|
.action(async (opts) => {
|
|
7963
7964
|
try {
|
|
7964
7965
|
const body = { name: opts.name, memberId: opts.member };
|
|
7965
7966
|
if (opts.description)
|
|
7966
7967
|
body.description = opts.description;
|
|
7968
|
+
// F45 — pass scope in POST body; 'project' preserves today's back-compat behavior
|
|
7969
|
+
body.scope = opts.scope;
|
|
7967
7970
|
const data = await api('POST', '/api/api-keys', body);
|
|
7971
|
+
const scope = data.apiKey.scope ?? opts.scope;
|
|
7968
7972
|
console.log(chalk.green(`API key created: ${opts.name}`));
|
|
7969
|
-
console.log(` Key:
|
|
7970
|
-
console.log(` ID:
|
|
7973
|
+
console.log(` Key: ${chalk.yellow(data.rawKey)}`);
|
|
7974
|
+
console.log(` ID: ${data.apiKey.id}`);
|
|
7975
|
+
console.log(` Scope: ${scope === 'org' ? chalk.cyan('org') : scope}`);
|
|
7971
7976
|
console.log('');
|
|
7972
7977
|
console.log(chalk.red(' ⚠ Save this key now — it will not be shown again.'));
|
|
7973
7978
|
}
|
|
7974
7979
|
catch (err) {
|
|
7975
|
-
|
|
7980
|
+
// F45 — map specific API error codes to friendly messages
|
|
7981
|
+
if (err.code === 'INSUFFICIENT_ROLE') {
|
|
7982
|
+
console.error(chalk.red('Error: Only org owners and admins can mint org-scoped keys'));
|
|
7983
|
+
}
|
|
7984
|
+
else if (err.code === 'INVALID_SCOPE') {
|
|
7985
|
+
console.error(chalk.red("Error: --scope must be 'org' or 'project'"));
|
|
7986
|
+
}
|
|
7987
|
+
else {
|
|
7988
|
+
console.error(chalk.red(err.message));
|
|
7989
|
+
}
|
|
7976
7990
|
process.exit(1);
|
|
7977
7991
|
}
|
|
7978
7992
|
});
|
|
@@ -7987,7 +8001,9 @@ apiKeys
|
|
|
7987
8001
|
return;
|
|
7988
8002
|
}
|
|
7989
8003
|
for (const k of data.apiKeys) {
|
|
7990
|
-
|
|
8004
|
+
// F45 — show scope field (org or project) alongside existing fields
|
|
8005
|
+
const scopeLabel = k.scope === 'org' ? ` [${chalk.cyan('org-scoped')}]` : '';
|
|
8006
|
+
console.log(` ${chalk.cyan(k.name)}${scopeLabel} — ${k.id} (created ${k.created_at})`);
|
|
7991
8007
|
}
|
|
7992
8008
|
}
|
|
7993
8009
|
catch (err) {
|
|
@@ -9136,11 +9152,61 @@ Types: question | task | signal | response | flag | decision | direction`)
|
|
|
9136
9152
|
`Run from inside a Fazemos-aware workspace.`);
|
|
9137
9153
|
}
|
|
9138
9154
|
// Resolve recipient
|
|
9155
|
+
// rul_dispatch_cli_fallthrough_on_local_unresolved
|
|
9139
9156
|
const resolved = resolveRole(to, localRegistry);
|
|
9140
9157
|
if (!resolved) {
|
|
9141
|
-
|
|
9142
|
-
|
|
9158
|
+
// Registry-less path: recipient not found in local workspace registry
|
|
9159
|
+
// (e.g. cross-workspace human role like `founder` in a peer workspace).
|
|
9160
|
+
// Fall through to server-side DB resolution via POST /api/dispatches.
|
|
9161
|
+
// The server resolves the recipient via role_registrations (workspace-agnostic).
|
|
9162
|
+
// rul_dispatch_cli_fallthrough_on_local_unresolved
|
|
9163
|
+
const unresolvedDepth = computeChildDispatchDepth(process.env.FAZEMOS_ORIGINATING_DISPATCH_DEPTH);
|
|
9164
|
+
const unresolvedReqBody = {
|
|
9165
|
+
to,
|
|
9166
|
+
from: opts.from,
|
|
9167
|
+
type,
|
|
9168
|
+
priority: opts.priority ?? 'normal',
|
|
9169
|
+
body,
|
|
9170
|
+
thread_id: opts.thread ?? null,
|
|
9171
|
+
re: opts.re ? [opts.re] : null,
|
|
9172
|
+
expires_at: opts.expiresAt ?? null,
|
|
9173
|
+
source: 'cli',
|
|
9174
|
+
runbook_ref: opts.runbookRef ?? null,
|
|
9175
|
+
params: runbookParams ?? null,
|
|
9176
|
+
dispatch_depth: unresolvedDepth,
|
|
9177
|
+
};
|
|
9178
|
+
let unresolvedResp;
|
|
9179
|
+
try {
|
|
9180
|
+
unresolvedResp = await api('POST', '/api/dispatches', unresolvedReqBody);
|
|
9181
|
+
}
|
|
9182
|
+
catch (err) {
|
|
9183
|
+
// rul_dispatch_4xx_still_hard_fails (BUG53 semantics preserved)
|
|
9184
|
+
// Deliberate 4xx (FROM_ROLE_FORBIDDEN, depth ceiling, validation) hard-fails
|
|
9185
|
+
// in the registry-less path too. No local file fallback — no role.inbox
|
|
9186
|
+
// available (no resolved role entry). Preserves the BUG53 loud-failure contract.
|
|
9187
|
+
// rul_dispatch_local_file_best_effort_cross_workspace
|
|
9188
|
+
const status = err instanceof ApiError ? err.status : undefined;
|
|
9189
|
+
if (!shouldFallBackToFile(status)) {
|
|
9190
|
+
const code = err instanceof ApiError && err.code ? ` ${err.code}` : '';
|
|
9191
|
+
console.error(chalk.red(`✗ Dispatch refused (${status}${code}): ${err instanceof Error ? err.message : String(err)}`));
|
|
9192
|
+
console.error(chalk.gray(' Not falling back to file-only — a deliberate refusal must not be bypassed.'));
|
|
9193
|
+
process.exitCode = 1;
|
|
9194
|
+
return;
|
|
9195
|
+
}
|
|
9196
|
+
// Transient failure (network / 5xx) — no local file fallback in registry-less path.
|
|
9197
|
+
// rul_dispatch_local_file_best_effort_cross_workspace
|
|
9198
|
+
throw err;
|
|
9199
|
+
}
|
|
9200
|
+
// 2xx — server resolved the recipient. Print UX §4.4 stdout copy verbatim.
|
|
9201
|
+
// rul_dispatch_local_file_best_effort_cross_workspace
|
|
9202
|
+
console.log(chalk.green(`✓ Dispatched to ${to} (server-resolved) — id: ${unresolvedResp.id}`));
|
|
9203
|
+
console.log(` Local inbox mirror skipped: '${to}' is not in this workspace's registry;`);
|
|
9204
|
+
console.log(` the durable artifact is the DB row + Slack notification.`);
|
|
9205
|
+
// SKIP local inbox-file write (no role.inbox) — rul_dispatch_local_file_best_effort_cross_workspace
|
|
9206
|
+
// SKIP agent-mode commit+push (no relPath) — rul_dispatch_local_file_best_effort_cross_workspace
|
|
9207
|
+
return;
|
|
9143
9208
|
}
|
|
9209
|
+
// ── Resolved path: existing behavior unchanged ──────────────────────────
|
|
9144
9210
|
const { role, registry } = resolved;
|
|
9145
9211
|
const input = {
|
|
9146
9212
|
to,
|