drafted 1.25.2 → 1.25.3
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/cli/drafted.mjs +73 -0
- package/package.json +1 -1
package/cli/drafted.mjs
CHANGED
|
@@ -2244,6 +2244,79 @@ async function minionSetEnabled(id, enabled, format) {
|
|
|
2244
2244
|
emitMinionResult(format, { status: 'ok', id: c.id, slug: c.slug, name: c.name });
|
|
2245
2245
|
}
|
|
2246
2246
|
|
|
2247
|
+
// ── Actions: propose an outward effect and read its decision ─────────────
|
|
2248
|
+
// The seam a Causeway automation's approval@1 step uses (Approvals inbox, A5/C4):
|
|
2249
|
+
// propose, then poll status until a person decides in the org's Approvals inbox.
|
|
2250
|
+
// Approving stays a browser-only act in Drafted; nothing here can approve.
|
|
2251
|
+
const actionCmd = program.command('action').description('Propose an outward effect for a person to approve, and read its decision');
|
|
2252
|
+
|
|
2253
|
+
function actionState(actions) {
|
|
2254
|
+
const st = (actions || []).map((a) => a.status);
|
|
2255
|
+
if (!st.length) return 'unknown';
|
|
2256
|
+
if (st.every((s) => s === 'proposed')) return 'waiting';
|
|
2257
|
+
if (st.includes('rejected')) return 'rejected';
|
|
2258
|
+
if (st.includes('failed')) return 'failed';
|
|
2259
|
+
if (st.every((s) => s === 'executed')) return 'executed';
|
|
2260
|
+
return 'approved';
|
|
2261
|
+
}
|
|
2262
|
+
|
|
2263
|
+
async function actionApi(method, apiPath, org, body) {
|
|
2264
|
+
requireLogin();
|
|
2265
|
+
const server = getServerUrl().replace(/\/$/, '');
|
|
2266
|
+
const headers = { ...(org ? { 'X-Drafted-Org': org } : {}), ...(body ? { 'Content-Type': 'application/json' } : {}) };
|
|
2267
|
+
const res = await authFetch(`${server}${apiPath}`, { method, headers, body: body ? JSON.stringify(body) : undefined });
|
|
2268
|
+
const data = await res.json().catch(() => ({}));
|
|
2269
|
+
return { ok: res.ok, status: res.status, data };
|
|
2270
|
+
}
|
|
2271
|
+
|
|
2272
|
+
actionCmd
|
|
2273
|
+
.command('propose')
|
|
2274
|
+
.description('Propose from JSON {type,payload} or {actions:[{type,payload}...]}, plus optional reason and assignee (an org member email or user id; default you). Reads --file or stdin.')
|
|
2275
|
+
.option('--file <path>', 'JSON file (default: stdin)')
|
|
2276
|
+
.option('--org <org>', 'org (id or slug) to propose in')
|
|
2277
|
+
.option('--format <fmt>', 'output format: json or text', 'text')
|
|
2278
|
+
.action(async (opts) => {
|
|
2279
|
+
let req;
|
|
2280
|
+
try { req = opts.file ? JSON.parse(readFileSync(opts.file, 'utf8')) : readStdinJSON(); }
|
|
2281
|
+
catch { console.error('invalid JSON request'); process.exit(1); }
|
|
2282
|
+
const body = Array.isArray(req.actions) ? { actions: req.actions } : { actions: [{ type: req.type, payload: req.payload || {} }] };
|
|
2283
|
+
if (req.reason) body.reason = String(req.reason);
|
|
2284
|
+
if (req.assignee) body.assignee = String(req.assignee);
|
|
2285
|
+
const r = await actionApi('POST', '/api/actions', opts.org, body);
|
|
2286
|
+
const out = r.ok
|
|
2287
|
+
? { status: 'ok', groupId: r.data.groupId, url: r.data.receipt && r.data.receipt.url, assignee: r.data.assignee || null }
|
|
2288
|
+
: { status: 'error', error: r.data.detail || r.data.error || `HTTP ${r.status}`, code: r.data.error || null };
|
|
2289
|
+
if (opts.format === 'json') console.log(JSON.stringify(out));
|
|
2290
|
+
else console.log(r.ok ? `${out.groupId}\t${out.url || ''}` : `error\t${out.error}`);
|
|
2291
|
+
if (!r.ok) process.exit(1);
|
|
2292
|
+
});
|
|
2293
|
+
|
|
2294
|
+
actionCmd
|
|
2295
|
+
.command('status <groupId>')
|
|
2296
|
+
.description('A proposal\'s decision: waiting, approved, executed, failed or rejected, with who decided and why')
|
|
2297
|
+
.option('--org <org>', 'org (id or slug) it was proposed in')
|
|
2298
|
+
.option('--format <fmt>', 'output format: json or text', 'text')
|
|
2299
|
+
.action(async (groupId, opts) => {
|
|
2300
|
+
const r = await actionApi('GET', `/api/actions/groups/${encodeURIComponent(groupId)}`, opts.org);
|
|
2301
|
+
if (!r.ok) {
|
|
2302
|
+
const out = { status: 'error', error: r.data.detail || r.data.error || `HTTP ${r.status}`, code: r.data.error || null };
|
|
2303
|
+
if (opts.format === 'json') console.log(JSON.stringify(out)); else console.log(`error\t${out.error}`);
|
|
2304
|
+
process.exit(1);
|
|
2305
|
+
}
|
|
2306
|
+
const acts = r.data.actions || [];
|
|
2307
|
+
const out = {
|
|
2308
|
+
status: 'ok',
|
|
2309
|
+
groupId,
|
|
2310
|
+
state: actionState(acts),
|
|
2311
|
+
decidedBy: r.data.decidedBy ? r.data.decidedBy.name : null,
|
|
2312
|
+
note: r.data.decisionNote || null,
|
|
2313
|
+
assignee: r.data.assignee ? r.data.assignee.name : null,
|
|
2314
|
+
steps: acts.map((a) => ({ seq: a.seq, type: a.type, status: a.status, error: (a.result && a.result.error) || null })),
|
|
2315
|
+
};
|
|
2316
|
+
if (opts.format === 'json') console.log(JSON.stringify(out));
|
|
2317
|
+
else console.log(`${out.state}\t${out.decidedBy || ''}\t${out.note || ''}`);
|
|
2318
|
+
});
|
|
2319
|
+
|
|
2247
2320
|
const minionCmd = program.command('minion').description('Minion management (checklist-driven intake surfaces). Requires the agent allowlist.');
|
|
2248
2321
|
|
|
2249
2322
|
minionCmd
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "drafted",
|
|
3
|
-
"version": "1.25.
|
|
3
|
+
"version": "1.25.3",
|
|
4
4
|
"description": "Drafted — visual thinking surface for humans and AI agents. Renders HTML, markdown, images, and code as frames on a zoomable canvas, with MCP tools for AI agents and real-time sync for humans.",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"bin": {
|