open-research-protocol 0.4.20 → 0.4.22

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/README.md CHANGED
@@ -326,6 +326,12 @@ For an agent or script, use stdin:
326
326
  printf '%s' 'sk-...' | orp secrets add --alias openai-primary --label "OpenAI Primary" --provider openai --value-stdin
327
327
  ```
328
328
 
329
+ If a service needs both a username and a secret, store the username with it:
330
+
331
+ ```bash
332
+ orp secrets add --alias huggingface-login --label "Hugging Face Login" --provider huggingface --kind password --username cody
333
+ ```
334
+
329
335
  After that:
330
336
 
331
337
  ```bash
@@ -356,6 +362,7 @@ does not contain the key itself. It means:
356
362
  For secrets, the simplest plain-English rule is:
357
363
 
358
364
  - `orp secrets add ...` = save a new key
365
+ - `orp secrets add ... --username <name>` = save a new login credential plus its username
359
366
  - `orp secrets list` = see what is saved
360
367
  - `orp secrets show ...` = inspect one saved key record
361
368
  - `orp secrets resolve ...` = get the key value for use right now
package/cli/orp.py CHANGED
@@ -3443,6 +3443,9 @@ def _hosted_api_error(
3443
3443
  payload: dict[str, Any] | None,
3444
3444
  ) -> HostedApiError:
3445
3445
  message = str((payload or {}).get("error") or (payload or {}).get("message") or f"Request failed: {status}")
3446
+ stripped_message = message.lstrip()
3447
+ if stripped_message.startswith("<!DOCTYPE html") or stripped_message.startswith("<html"):
3448
+ message = "Hosted ORP returned an HTML error page instead of JSON"
3446
3449
  suffix = f" (status={status} path={path})"
3447
3450
  hint = ""
3448
3451
  if status == 401:
@@ -3450,7 +3453,13 @@ def _hosted_api_error(
3450
3453
  elif status == 403:
3451
3454
  hint = " The hosted ORP app rejected the operation. Check permissions on the target record."
3452
3455
  elif status == 404:
3453
- hint = " The hosted record may have changed. Re-list the resource and retry."
3456
+ if message == "Hosted ORP returned an HTML error page instead of JSON":
3457
+ hint = (
3458
+ " The hosted API route may not be deployed at this base URL. "
3459
+ "Check ORP_BASE_URL or deploy the hosted ORP app."
3460
+ )
3461
+ else:
3462
+ hint = " The hosted record may have changed. Re-list the resource and retry."
3454
3463
  elif status == 409:
3455
3464
  hint = " The hosted record changed since you last fetched it. Re-open it and retry the update."
3456
3465
  return HostedApiError(f"{message}{suffix}.{hint}".replace("..", "."))
@@ -10736,7 +10745,7 @@ def _about_payload() -> dict[str, Any]:
10736
10745
  "Frontier control is a built-in ORP ability exposed through `orp frontier ...`, separating the exact live point, the exact active milestone, the near structured checklist, and the farther major-version stack.",
10737
10746
  "Agent modes are lightweight optional overlays for taste, perspective shifts, and fresh movement; `orp mode nudge sleek-minimal-progressive --json` gives agents a deterministic reminder they can call on when they want a deeper, wider, top-down, or rotated lens without changing ORP's core artifact boundaries.",
10738
10747
  "Project/session linking is a built-in ORP ability exposed through `orp link ...` and stored machine-locally under `.git/orp/link/`.",
10739
- "Secrets are easiest to understand as saved keys and tokens: humans usually run `orp secrets add ...` and paste the value at the prompt, agents usually pipe the value with `--value-stdin`, and local macOS Keychain caching plus hosted sync are optional layers on top.",
10748
+ "Secrets are easiest to understand as saved credentials and related login metadata: humans usually run `orp secrets add ...` and paste the value at the prompt, agents usually pipe the value with `--value-stdin`, optional usernames can be stored alongside the secret when a service needs them, and local macOS Keychain caching plus hosted sync are optional layers on top.",
10740
10749
  "Connections give ORP one place to remember service accounts, public data sources, deployment targets, and which saved secret alias or named secret bindings power each integration through `orp connections providers`, `orp connections list`, `orp connections show`, `orp connections add`, `orp connections update`, `orp connections remove`, `orp connections sync`, and `orp connections pull`.",
10741
10750
  "Agenda refresh is built into ORP through `orp agenda refresh`, `orp agenda actions`, `orp agenda suggestions`, `orp agenda focus`, and `orp agenda set-north-star`, using a Codex reasoning pass over current workspace, GitHub, opportunities, and connection context to keep a ranked action list and a ranked suggestion list.",
10742
10751
  "Recurring agenda refreshes are always explicit opt-in. Nothing runs on a schedule until the user enables it with `orp agenda enable-refreshes`; `orp agenda refresh-status` shows the current state and default morning/afternoon/evening presets.",
@@ -10998,7 +11007,7 @@ def _home_payload(repo_root: Path, config_arg: str) -> dict[str, Any]:
10998
11007
  "command": "orp workspaces list --json",
10999
11008
  },
11000
11009
  {
11001
- "label": "Inspect saved keys and tokens already known to ORP",
11010
+ "label": "Inspect saved credentials and usernames already known to ORP",
11002
11011
  "command": "orp secrets list --json",
11003
11012
  },
11004
11013
  {
@@ -11365,7 +11374,7 @@ def _home_payload(repo_root: Path, config_arg: str) -> dict[str, Any]:
11365
11374
  },
11366
11375
  {
11367
11376
  "id": "secrets",
11368
- "description": "Saved API keys and tokens, with an interactive human flow, a stdin agent flow, optional local macOS Keychain caching, and optional hosted sync.",
11377
+ "description": "Saved API keys, tokens, passwords, and optional usernames, with an interactive human flow, a stdin agent flow, optional local macOS Keychain caching, and optional hosted sync.",
11369
11378
  "entrypoints": [
11370
11379
  "orp secrets list --json",
11371
11380
  "orp secrets show <alias-or-id> --json",
@@ -18573,6 +18582,7 @@ def _print_secret_human(
18573
18582
  ("secret.label", str(secret.get("label", "")).strip()),
18574
18583
  ("secret.provider", str(secret.get("provider", "")).strip()),
18575
18584
  ("secret.kind", str(secret.get("kind", "")).strip()),
18585
+ ("secret.username", str(secret.get("username", "")).strip()),
18576
18586
  ("secret.env_var_name", str(secret.get("envVarName", "")).strip()),
18577
18587
  ("secret.preview", str(secret.get("valuePreview", "")).strip()),
18578
18588
  ("secret.version", str(secret.get("valueVersion", "")).strip()),
@@ -18630,6 +18640,7 @@ def _keychain_comment_for_secret(secret: dict[str, Any]) -> str:
18630
18640
  "secret_id": str(secret.get("id", "")).strip(),
18631
18641
  "alias": str(secret.get("alias", "")).strip(),
18632
18642
  "provider": str(secret.get("provider", "")).strip(),
18643
+ "username": str(secret.get("username", "")).strip(),
18633
18644
  "env_var_name": str(secret.get("envVarName", "")).strip(),
18634
18645
  }
18635
18646
  return json.dumps(payload, sort_keys=True)
@@ -18692,6 +18703,7 @@ def _build_keychain_registry_entry(
18692
18703
  "label": str(secret.get("label", "")).strip(),
18693
18704
  "provider": str(secret.get("provider", "")).strip(),
18694
18705
  "kind": str(secret.get("kind", "")).strip(),
18706
+ "username": str(secret.get("username", "")).strip(),
18695
18707
  "env_var_name": str(secret.get("envVarName", "")).strip(),
18696
18708
  "status": str(secret.get("status", "")).strip(),
18697
18709
  "value_version": str(secret.get("valueVersion", "")).strip(),
@@ -18712,6 +18724,7 @@ def _secret_payload_from_keychain_entry(entry: dict[str, Any]) -> dict[str, Any]
18712
18724
  "label": str(entry.get("label", "")).strip(),
18713
18725
  "provider": str(entry.get("provider", "")).strip(),
18714
18726
  "kind": str(entry.get("kind", "")).strip(),
18727
+ "username": str(entry.get("username", "")).strip(),
18715
18728
  "envVarName": str(entry.get("env_var_name", "")).strip(),
18716
18729
  "status": str(entry.get("status", "")).strip(),
18717
18730
  "valueVersion": str(entry.get("value_version", "")).strip(),
@@ -20371,6 +20384,10 @@ def cmd_secrets_add(args: argparse.Namespace) -> int:
20371
20384
  "kind": str(getattr(args, "kind", "api_key")).strip() or "api_key",
20372
20385
  "value": value,
20373
20386
  }
20387
+ username = getattr(args, "username", None)
20388
+ if username is not None:
20389
+ text = str(username).strip()
20390
+ body["username"] = text or None
20374
20391
  env_var_name = getattr(args, "env_var_name", None)
20375
20392
  if env_var_name is not None:
20376
20393
  text = str(env_var_name).strip()
@@ -20427,6 +20444,10 @@ def cmd_secrets_ensure(args: argparse.Namespace) -> int:
20427
20444
  "kind": str(getattr(args, "kind", "api_key")).strip() or "api_key",
20428
20445
  "value": value,
20429
20446
  }
20447
+ username = getattr(args, "username", None)
20448
+ if username is not None:
20449
+ text = str(username).strip()
20450
+ body["username"] = text or None
20430
20451
  env_var_name = getattr(args, "env_var_name", None)
20431
20452
  if env_var_name is not None:
20432
20453
  text = str(env_var_name).strip()
@@ -20509,6 +20530,7 @@ def cmd_secrets_update(args: argparse.Namespace) -> int:
20509
20530
  ("label", "label"),
20510
20531
  ("provider", "provider"),
20511
20532
  ("kind", "kind"),
20533
+ ("username", "username"),
20512
20534
  ("env_var_name", "envVarName"),
20513
20535
  ("notes", "notes"),
20514
20536
  ("status", "status"),
@@ -20516,7 +20538,7 @@ def cmd_secrets_update(args: argparse.Namespace) -> int:
20516
20538
  value = getattr(args, attr_name, None)
20517
20539
  if value is not None:
20518
20540
  text = str(value).strip()
20519
- if body_key in {"envVarName", "notes"}:
20541
+ if body_key in {"username", "envVarName", "notes"}:
20520
20542
  body[body_key] = text or None
20521
20543
  else:
20522
20544
  body[body_key] = text
@@ -23197,9 +23219,9 @@ def build_parser() -> argparse.ArgumentParser:
23197
23219
 
23198
23220
  s_secrets = sub.add_parser(
23199
23221
  "secrets",
23200
- help="Save and reuse API keys and tokens locally, with optional hosted sync",
23222
+ help="Save and reuse API keys, tokens, passwords, and related login usernames",
23201
23223
  description=(
23202
- "ORP secrets are easiest to understand as saved keys and tokens.\n\n"
23224
+ "ORP secrets are easiest to understand as saved credentials and related login metadata.\n\n"
23203
23225
  "Human flow:\n"
23204
23226
  " 1. Run `orp secrets add ...`\n"
23205
23227
  " 2. Paste the value when ORP prompts `Secret value:`\n"
@@ -23211,6 +23233,7 @@ def build_parser() -> argparse.ArgumentParser:
23211
23233
  epilog=(
23212
23234
  "Examples:\n"
23213
23235
  " orp secrets add --alias openai-primary --label \"OpenAI Primary\" --provider openai\n"
23236
+ " orp secrets add --alias huggingface-login --label \"Hugging Face Login\" --provider huggingface --kind password --username cody\n"
23214
23237
  " printf '%s' 'sk-...' | orp secrets add --alias openai-primary --label \"OpenAI Primary\" --provider openai --value-stdin\n"
23215
23238
  " orp secrets list\n"
23216
23239
  " orp secrets resolve openai-primary --reveal"
@@ -23250,6 +23273,11 @@ def build_parser() -> argparse.ArgumentParser:
23250
23273
  default="api_key",
23251
23274
  help="Secret kind (default: api_key)",
23252
23275
  )
23276
+ s_secrets_add.add_argument(
23277
+ "--username",
23278
+ default=None,
23279
+ help="Optional username or login identifier that belongs with this credential",
23280
+ )
23253
23281
  s_secrets_add.add_argument("--env-var-name", default=None, help="Optional env var name, for example OPENAI_API_KEY")
23254
23282
  s_secrets_add.add_argument("--value", default=None, help="Secret value")
23255
23283
  s_secrets_add.add_argument(
@@ -23282,6 +23310,11 @@ def build_parser() -> argparse.ArgumentParser:
23282
23310
  default="api_key",
23283
23311
  help="Secret kind when create-if-missing is needed (default: api_key)",
23284
23312
  )
23313
+ s_secrets_ensure.add_argument(
23314
+ "--username",
23315
+ default=None,
23316
+ help="Optional username or login identifier to store on create-if-missing flows",
23317
+ )
23285
23318
  s_secrets_ensure.add_argument(
23286
23319
  "--env-var-name",
23287
23320
  default=None,
@@ -23359,6 +23392,7 @@ def build_parser() -> argparse.ArgumentParser:
23359
23392
  default=None,
23360
23393
  help="Secret kind",
23361
23394
  )
23395
+ s_secrets_update.add_argument("--username", default=None, help="Updated username or login identifier")
23362
23396
  s_secrets_update.add_argument("--env-var-name", default=None, help="Updated env var name")
23363
23397
  s_secrets_update.add_argument("--value", default=None, help="New secret value")
23364
23398
  s_secrets_update.add_argument(
@@ -61,6 +61,7 @@ read:
61
61
  - If the task needs an API key or token that is not already available, save it first:
62
62
  - human interactive path:
63
63
  - `orp secrets add --alias <alias> --label "<label>" --provider <provider>`
64
+ - `orp secrets add --alias <alias> --label "<label>" --provider <provider> --kind password --username <login>`
64
65
  - agent/script path:
65
66
  - `printf '%s' '<secret>' | orp secrets add --alias <alias> --label "<label>" --provider <provider> --value-stdin`
66
67
  - convenience path:
@@ -441,6 +441,17 @@ That is the clearest beginner flow:
441
441
  4. list or show it later
442
442
  5. resolve it when you need to use it
443
443
 
444
+ If the service needs a username too, save that alongside the secret:
445
+
446
+ ```bash
447
+ orp secrets add --alias huggingface-login --label "Hugging Face Login" --provider huggingface --kind password --username cody
448
+ ```
449
+
450
+ That means ORP can remember both:
451
+
452
+ - the secret value
453
+ - the related username/login identifier
454
+
444
455
  #### Agent or script flow
445
456
 
446
457
  If an agent or script needs to save a key non-interactively, use stdin:
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "open-research-protocol",
3
- "version": "0.4.20",
3
+ "version": "0.4.22",
4
4
  "description": "ORP CLI (Open Research Protocol): workspace ledgers, secrets, scheduling, governed execution, and agent-friendly research workflows.",
5
5
  "license": "MIT",
6
6
  "author": "Fractal Research Group <cody@frg.earth>",